Compare commits
36 Commits
94d259e92a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
222f32d98f | ||
|
|
454ded8b26 | ||
|
|
9e85ba1fa4 | ||
|
|
2fb8d559a6 | ||
|
|
b0a103d8ce | ||
|
|
cb056e010f | ||
|
|
de15cb4206 | ||
|
|
f796cac6be | ||
|
|
31580df919 | ||
|
|
9a17209db2 | ||
|
|
04656790d2 | ||
|
|
25a0bd3431 | ||
|
|
6abbd4652a | ||
|
|
8369d10bab | ||
|
|
bdfe0d1594 | ||
|
|
034f2ead1c | ||
|
|
06c3891045 | ||
|
|
f09cbe6939 | ||
|
|
cd9e1e7242 | ||
|
|
966bad98d3 | ||
|
|
2b89fb7ede | ||
|
|
0fc88323ea | ||
|
|
96eba8270c | ||
|
|
a36c05994c | ||
|
|
ef78a85b9c | ||
|
|
f368da9e73 | ||
|
|
4f89ed3082 | ||
|
|
0d8152914a | ||
|
|
12809623c1 | ||
|
|
a29bb63a1d | ||
|
|
9e95194627 | ||
|
|
451fb206a6 | ||
|
|
e3c49effdb | ||
|
|
5c40249a18 | ||
|
|
b645f55f57 | ||
|
|
838fbe1b50 |
@@ -31,3 +31,4 @@ PANEL_BASE_URL=http://localhost:3000
|
|||||||
# Use a non-root user (see shared/scripts/setup-server.sh)
|
# Use a non-root user (see shared/scripts/setup-server.sh)
|
||||||
VPS_USER=deploy
|
VPS_USER=deploy
|
||||||
VPS_HOST=your-vps-ip
|
VPS_HOST=your-vps-ip
|
||||||
|
SESSION_SECRET=change-me-to-a-random-string
|
||||||
132
.gitea/workflows/ci-deploy.yml
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
name: CI / Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:17-alpine
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: aurora_test
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
run: |
|
||||||
|
curl -fsSL https://bun.sh/install | bash
|
||||||
|
echo "$HOME/.bun/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Create Config File
|
||||||
|
run: |
|
||||||
|
mkdir -p shared/config
|
||||||
|
cat <<EOF > shared/config/config.json
|
||||||
|
{
|
||||||
|
"leveling": { "base": 100, "exponent": 2.5, "chat": { "cooldownMs": 60000, "minXp": 15, "maxXp": 25 } },
|
||||||
|
"economy": {
|
||||||
|
"daily": { "amount": "100", "streakBonus": "10", "weeklyBonus": "50", "cooldownMs": 86400000 },
|
||||||
|
"transfers": { "allowSelfTransfer": false, "minAmount": "1" },
|
||||||
|
"exam": { "multMin": 0.05, "multMax": 0.03 }
|
||||||
|
},
|
||||||
|
"inventory": { "maxStackSize": "99", "maxSlots": 50 },
|
||||||
|
"commands": {},
|
||||||
|
"lootdrop": {
|
||||||
|
"activityWindowMs": 120000, "minMessages": 1, "spawnChance": 1, "cooldownMs": 3000,
|
||||||
|
"reward": { "min": 40, "max": 150, "currency": "Astral Units" }
|
||||||
|
},
|
||||||
|
"studentRole": "123", "visitorRole": "456", "colorRoles": [],
|
||||||
|
"moderation": {
|
||||||
|
"prune": { "maxAmount": 100, "confirmThreshold": 50, "batchSize": 100, "batchDelayMs": 1000 },
|
||||||
|
"cases": { "dmOnWarn": false }
|
||||||
|
},
|
||||||
|
"trivia": {
|
||||||
|
"entryFee": "50", "rewardMultiplier": 1.5, "timeoutSeconds": 30, "cooldownMs": 60000,
|
||||||
|
"categories": [], "difficulty": "random"
|
||||||
|
},
|
||||||
|
"system": {}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Typecheck
|
||||||
|
run: bunx tsc --noEmit
|
||||||
|
|
||||||
|
- name: Build Panel
|
||||||
|
run: bun run panel:build
|
||||||
|
|
||||||
|
- name: Setup Test Database
|
||||||
|
run: bun run db:push:local
|
||||||
|
env:
|
||||||
|
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/aurora_test
|
||||||
|
DISCORD_BOT_TOKEN: test_token
|
||||||
|
DISCORD_CLIENT_ID: "123"
|
||||||
|
DISCORD_GUILD_ID: "123"
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
run: |
|
||||||
|
cat <<EOF > .env.test
|
||||||
|
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/aurora_test"
|
||||||
|
DISCORD_BOT_TOKEN="test_token"
|
||||||
|
DISCORD_CLIENT_ID="123456789"
|
||||||
|
DISCORD_GUILD_ID="123456789"
|
||||||
|
DISCORD_CLIENT_SECRET="test-client-secret"
|
||||||
|
SESSION_SECRET="test-session-secret"
|
||||||
|
ADMIN_TOKEN="admin_token_123"
|
||||||
|
LOG_LEVEL="error"
|
||||||
|
EOF
|
||||||
|
bash shared/scripts/test-isolated.sh --integration
|
||||||
|
env:
|
||||||
|
NODE_ENV: test
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
needs: test
|
||||||
|
if: gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Configure SSH
|
||||||
|
env:
|
||||||
|
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||||
|
VPS_HOST: ${{ secrets.VPS_HOST }}
|
||||||
|
run: |
|
||||||
|
install -m 700 -d ~/.ssh
|
||||||
|
printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
||||||
|
chmod 600 ~/.ssh/id_ed25519
|
||||||
|
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
|
- name: Deploy on VPS
|
||||||
|
env:
|
||||||
|
VPS_HOST: ${{ secrets.VPS_HOST }}
|
||||||
|
VPS_USER: ${{ secrets.VPS_USER }}
|
||||||
|
VPS_PROJECT_PATH: ${{ secrets.VPS_PROJECT_PATH }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
REMOTE_DIR="${VPS_PROJECT_PATH:-~/Aurora}"
|
||||||
|
ssh -o BatchMode=yes "$VPS_USER@$VPS_HOST" "cd $REMOTE_DIR && bash shared/scripts/deploy.sh"
|
||||||
|
|
||||||
|
- name: Post-deploy Health Check
|
||||||
|
env:
|
||||||
|
VPS_HOST: ${{ secrets.VPS_HOST }}
|
||||||
|
VPS_USER: ${{ secrets.VPS_USER }}
|
||||||
|
VPS_PROJECT_PATH: ${{ secrets.VPS_PROJECT_PATH }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
REMOTE_DIR="${VPS_PROJECT_PATH:-~/Aurora}"
|
||||||
|
ssh -o BatchMode=yes "$VPS_USER@$VPS_HOST" "cd $REMOTE_DIR && curl -fsS http://127.0.0.1:3000/api/health >/dev/null"
|
||||||
4
.github/workflows/deploy.yml
vendored
@@ -86,7 +86,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: |
|
run: |
|
||||||
# Create .env.test for test-sequential.sh / bun test
|
# Create .env.test for the isolated test runner / bun test
|
||||||
cat <<EOF > .env.test
|
cat <<EOF > .env.test
|
||||||
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/aurora_test"
|
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/aurora_test"
|
||||||
DISCORD_BOT_TOKEN="test_token"
|
DISCORD_BOT_TOKEN="test_token"
|
||||||
@@ -95,6 +95,6 @@ jobs:
|
|||||||
ADMIN_TOKEN="admin_token_123"
|
ADMIN_TOKEN="admin_token_123"
|
||||||
LOG_LEVEL="error"
|
LOG_LEVEL="error"
|
||||||
EOF
|
EOF
|
||||||
bash shared/scripts/test-sequential.sh --integration
|
bash shared/scripts/test-isolated.sh --integration
|
||||||
env:
|
env:
|
||||||
NODE_ENV: test
|
NODE_ENV: test
|
||||||
|
|||||||
312
AGENTS.md
@@ -1,257 +1,135 @@
|
|||||||
# AGENTS.md - AI Coding Agent Guidelines
|
# AGENTS.md
|
||||||
|
|
||||||
## Project Overview
|
This file documents the current implementation shape of the Aurora repository.
|
||||||
|
|
||||||
AuroraBot is a Discord bot with a REST API built using Bun, Discord.js, and PostgreSQL with Drizzle ORM.
|
## Commands
|
||||||
|
|
||||||
## Build/Lint/Test Commands
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Development
|
# App
|
||||||
bun --watch bot/index.ts # Run bot + API server with hot reload
|
bun run dev # bot + API in one Bun process with watch mode
|
||||||
|
docker compose up # app + db
|
||||||
|
docker compose up app # app only
|
||||||
|
docker compose up db # database only
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
bun test # Run all tests
|
bun test # Bun's native runner
|
||||||
bun test path/to/file.test.ts # Run single test file
|
bun run test # repo test wrapper script
|
||||||
bun test --watch # Watch mode
|
bun run test:ci # include CI/integration path
|
||||||
bun test shared/modules/economy # Run tests in directory
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
bun run generate # Generate Drizzle migrations (Docker)
|
bun run db:push # drizzle-kit push via Docker
|
||||||
bun run migrate # Run migrations (Docker)
|
bun run db:push:local # drizzle-kit push locally
|
||||||
bun run db:push # Push schema changes (Docker)
|
bun run db:generate # drizzle-kit generate via Docker
|
||||||
bun run db:push:local # Push schema changes (local)
|
bun run db:migrate # drizzle-kit migrate via Docker
|
||||||
bun run db:studio # Open Drizzle Studio
|
bun run db:studio # local Drizzle Studio on :4983
|
||||||
|
|
||||||
# Docker (recommended for local dev)
|
# Panel
|
||||||
docker compose up # Start bot, API, and database
|
bun run panel:dev # Vite dev server on :5173
|
||||||
docker compose up app # Start just the app (bot + API)
|
bun run panel:build # build panel/dist
|
||||||
docker compose up db # Start just the database
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Project Structure
|
## Architecture
|
||||||
|
|
||||||
```
|
Aurora is a single-process Bun application:
|
||||||
bot/ # Discord bot
|
|
||||||
├── commands/ # Slash commands by category
|
|
||||||
├── events/ # Discord event handlers
|
|
||||||
├── lib/ # Bot core (BotClient, handlers, loaders)
|
|
||||||
├── modules/ # Feature modules (views, interactions)
|
|
||||||
└── graphics/ # Canvas image generation
|
|
||||||
|
|
||||||
shared/ # Shared between bot and web
|
- `bot/index.ts` boots shared config, registers domain listeners, starts the API server, then logs into Discord.
|
||||||
├── db/ # Database schema and migrations
|
- `api/src/server.ts` hosts REST routes, WebSocket traffic, and built panel assets.
|
||||||
├── lib/ # Utils, config, errors, types
|
- `shared/modules/*` contains the business logic used by both the bot and the API.
|
||||||
└── modules/ # Domain services (economy, user, etc.)
|
- `shared/games/*` contains reusable game plugins; `api/src/games/*` runs rooms and WebSocket orchestration.
|
||||||
|
|
||||||
web/ # API server
|
Current high-level layout:
|
||||||
└── src/routes/ # API route handlers
|
|
||||||
|
```text
|
||||||
|
bot/ Discord commands, events, views, interactions
|
||||||
|
api/ Bun HTTP + WebSocket server
|
||||||
|
panel/ React dashboard
|
||||||
|
shared/db/ Drizzle client and schema
|
||||||
|
shared/lib/ config, env, errors, logger, events, constants
|
||||||
|
shared/modules/ domain services
|
||||||
|
shared/games/ game plugins shared by API and panel
|
||||||
```
|
```
|
||||||
|
|
||||||
## Import Conventions
|
## Import conventions
|
||||||
|
|
||||||
Use path aliases defined in tsconfig.json:
|
Use path aliases from the repo `tsconfig.json`:
|
||||||
|
|
||||||
```typescript
|
- `@/*` -> `bot/*`
|
||||||
// External packages first
|
- `@commands/*` -> `bot/commands/*`
|
||||||
import { SlashCommandBuilder } from "discord.js";
|
- `@db/*` -> `shared/db/*`
|
||||||
import { eq } from "drizzle-orm";
|
- `@lib/*` -> `bot/lib/*`
|
||||||
|
- `@modules/*` -> `bot/modules/*`
|
||||||
|
- `@shared/*` -> `shared/*`
|
||||||
|
|
||||||
// Path aliases second
|
Import order in the repo is generally:
|
||||||
import { economyService } from "@shared/modules/economy/economy.service";
|
|
||||||
import { UserError } from "@shared/lib/errors";
|
|
||||||
import { users } from "@db/schema";
|
|
||||||
import { createErrorEmbed } from "@lib/embeds";
|
|
||||||
import { handleTradeInteraction } from "@modules/trade/trade.interaction";
|
|
||||||
|
|
||||||
// Relative imports last
|
1. external packages
|
||||||
import { localHelper } from "./helper";
|
2. aliases
|
||||||
```
|
3. relative imports
|
||||||
|
|
||||||
**Available Aliases:**
|
## File patterns
|
||||||
|
|
||||||
- `@/*` - bot/
|
- `*.service.ts`: domain/business logic, usually in `shared/modules/*`
|
||||||
- `@shared/*` - shared/
|
- `*.view.ts`: Discord message/view construction
|
||||||
- `@db/*` - shared/db/
|
- `*.interaction.ts`: component interaction handlers
|
||||||
- `@lib/*` - bot/lib/
|
- `*.types.ts`: local types and custom ID helpers
|
||||||
- `@modules/*` - bot/modules/
|
- `*.handler.ts`: bot-side orchestration around services/views
|
||||||
- `@commands/*` - bot/commands/
|
- `*.test.ts`: colocated tests
|
||||||
|
|
||||||
## Naming Conventions
|
## Runtime config
|
||||||
|
|
||||||
| Element | Convention | Example |
|
- Global game settings live in `game_settings` and are loaded into `shared/lib/config.ts`.
|
||||||
| ---------------- | ----------------------- | ---------------------------------------- |
|
- Guild-specific settings live in `guild_settings`; `getGuildConfig()` adds a 60-second cache on top of DB reads.
|
||||||
| Files | camelCase or kebab-case | `BotClient.ts`, `economy.service.ts` |
|
- Most numeric DB values exposed through runtime config are converted to `bigint` in `shared/lib/config.ts`.
|
||||||
| Classes | PascalCase | `CommandHandler`, `UserError` |
|
|
||||||
| Functions | camelCase | `createCommand`, `handleShopInteraction` |
|
|
||||||
| Constants | UPPER_SNAKE_CASE | `EVENTS`, `BRANDING` |
|
|
||||||
| Enums | PascalCase | `TimerType`, `TransactionType` |
|
|
||||||
| Services | camelCase singleton | `economyService`, `userService` |
|
|
||||||
| Types/Interfaces | PascalCase | `Command`, `Event`, `GameConfigType` |
|
|
||||||
| DB tables | snake_case | `users`, `moderation_cases` |
|
|
||||||
| Custom IDs | snake_case with prefix | `shop_buy_`, `trade_accept_` |
|
|
||||||
|
|
||||||
## Code Patterns
|
## Interaction routing
|
||||||
|
|
||||||
### Command Definition
|
Global component routing is defined in `bot/lib/interaction.routes.ts` and consumed by `ComponentInteractionHandler`.
|
||||||
|
|
||||||
```typescript
|
Current route table:
|
||||||
export const commandName = createCommand({
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("commandname")
|
|
||||||
.setDescription("Description"),
|
|
||||||
execute: async (interaction) => {
|
|
||||||
await interaction.deferReply();
|
|
||||||
// Implementation
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Service Pattern (Singleton Object)
|
- `trade_` and `amount` -> `bot/modules/trade/trade.interaction.ts`
|
||||||
|
- `shop_buy_` -> `bot/modules/economy/shop.interaction.ts`
|
||||||
|
- `lootdrop_` -> `bot/modules/economy/lootdrop.interaction.ts`
|
||||||
|
- `trivia_` -> `bot/modules/trivia/trivia.interaction.ts`
|
||||||
|
- `createitem_` -> `bot/modules/admin/item_wizard.ts`
|
||||||
|
- `enrollment` -> `bot/modules/user/enrollment.interaction.ts`
|
||||||
|
- `feedback_` -> `bot/modules/feedback/feedback.interaction.ts`
|
||||||
|
|
||||||
```typescript
|
Some features still use local collectors instead of the global route table, notably inventory.
|
||||||
export const serviceName = {
|
|
||||||
methodName: async (params: ParamType): Promise<ReturnType> => {
|
|
||||||
return await withTransaction(async (tx) => {
|
|
||||||
// Database operations
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Module File Organization
|
## Commands and access control
|
||||||
|
|
||||||
- `*.view.ts` - Creates Discord embeds/components
|
- Slash command execution is centralized in `bot/lib/handlers/CommandHandler.ts`.
|
||||||
- `*.interaction.ts` - Handles button/select/modal interactions
|
- `withCommandErrorHandling()` is the normal command wrapper for defer/reply/error behavior.
|
||||||
- `*.types.ts` - Module-specific TypeScript types
|
- Beta commands rely on `featureFlagsService.hasAccess()`.
|
||||||
- `*.service.ts` - Business logic (in shared/modules/)
|
- `ADMIN_USER_IDS` controls admin panel access, not Discord permissions inside command code.
|
||||||
- `*.test.ts` - Test files (co-located with source)
|
|
||||||
|
|
||||||
## Error Handling
|
## API and panel
|
||||||
|
|
||||||
### Custom Error Classes
|
- API routes are prefix-matched in `api/src/routes/index.ts`.
|
||||||
|
- `/auth/*` and `/api/health` are public.
|
||||||
|
- Players may access `/api/stats`, `/api/health`, `/api/me`, and `/api/me/inventory`.
|
||||||
|
- Remaining `/api/*` routes are admin-only.
|
||||||
|
- The panel dev server proxies back to the Bun server; the integrated server serves `panel/dist` when built.
|
||||||
|
|
||||||
```typescript
|
## Database notes
|
||||||
import { UserError, SystemError } from "@shared/lib/errors";
|
|
||||||
|
|
||||||
// User-facing errors (shown to user)
|
- Docker Compose uses PostgreSQL 17.
|
||||||
throw new UserError("You don't have enough coins!");
|
- Discord IDs and currency/xp values are stored as `bigint`.
|
||||||
|
- `withTransaction()` lives in `bot/lib/db.ts` and is the normal way shared services compose DB work.
|
||||||
// System errors (logged, generic message shown)
|
|
||||||
throw new SystemError("Database connection failed");
|
|
||||||
```
|
|
||||||
|
|
||||||
### Recommended: `withCommandErrorHandling`
|
|
||||||
|
|
||||||
Use the `withCommandErrorHandling` utility from `@lib/commandUtils` to standardize
|
|
||||||
error handling across all commands. It handles `deferReply`, `UserError` display,
|
|
||||||
and unexpected error logging automatically.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { withCommandErrorHandling } from "@lib/commandUtils";
|
|
||||||
|
|
||||||
export const myCommand = createCommand({
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName("mycommand")
|
|
||||||
.setDescription("Does something"),
|
|
||||||
execute: async (interaction) => {
|
|
||||||
await withCommandErrorHandling(
|
|
||||||
interaction,
|
|
||||||
async () => {
|
|
||||||
const result = await service.method();
|
|
||||||
await interaction.editReply({ embeds: [createSuccessEmbed(result)] });
|
|
||||||
},
|
|
||||||
{ ephemeral: true } // optional: makes the deferred reply ephemeral
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Options:
|
|
||||||
- `ephemeral` — whether `deferReply` should be ephemeral
|
|
||||||
- `successMessage` — a simple string to send on success
|
|
||||||
- `onSuccess` — a callback invoked with the operation result
|
|
||||||
```
|
|
||||||
|
|
||||||
## Database Patterns
|
|
||||||
|
|
||||||
### Transaction Usage
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { withTransaction } from "@/lib/db";
|
|
||||||
|
|
||||||
return await withTransaction(async (tx) => {
|
|
||||||
const user = await tx.query.users.findFirst({
|
|
||||||
where: eq(users.id, discordId),
|
|
||||||
});
|
|
||||||
|
|
||||||
await tx
|
|
||||||
.update(users)
|
|
||||||
.set({ coins: newBalance })
|
|
||||||
.where(eq(users.id, discordId));
|
|
||||||
await tx.insert(transactions).values({ userId: discordId, amount, type });
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}, existingTx); // Pass existing tx if in nested transaction
|
|
||||||
```
|
|
||||||
|
|
||||||
### Schema Notes
|
|
||||||
|
|
||||||
- Use `bigint` mode for Discord IDs and currency amounts
|
|
||||||
- Relations defined separately from table definitions
|
|
||||||
- Schema modules: `shared/db/schema/*.ts` (users, inventory, economy, quests, moderation)
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
### Test File Structure
|
- Tests use `bun:test`.
|
||||||
|
- Mock modules before importing the unit under test.
|
||||||
|
- Most service tests stub `DrizzleClient` or `withTransaction()` rather than hitting the real database.
|
||||||
|
|
||||||
```typescript
|
## Key entrypoints
|
||||||
import { describe, it, expect, mock, beforeEach } from "bun:test";
|
|
||||||
|
|
||||||
// Mock modules BEFORE imports
|
- `bot/index.ts`
|
||||||
mock.module("@shared/db/DrizzleClient", () => ({
|
- `bot/lib/BotClient.ts`
|
||||||
DrizzleClient: { query: mockQuery },
|
- `api/src/server.ts`
|
||||||
}));
|
- `api/src/routes/index.ts`
|
||||||
|
- `shared/lib/config.ts`
|
||||||
describe("serviceName", () => {
|
- `shared/db/DrizzleClient.ts`
|
||||||
beforeEach(() => {
|
- `shared/db/schema/index.ts`
|
||||||
mockFn.mockClear();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle expected case", async () => {
|
|
||||||
// Arrange
|
|
||||||
mockFn.mockResolvedValue(testData);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = await service.method(input);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toEqual(expected);
|
|
||||||
expect(mockFn).toHaveBeenCalledWith(expectedArgs);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tech Stack
|
|
||||||
|
|
||||||
- **Runtime:** Bun 1.0+
|
|
||||||
- **Bot:** Discord.js 14.x
|
|
||||||
- **Web:** Bun HTTP Server (REST API)
|
|
||||||
- **Database:** PostgreSQL 16+ with Drizzle ORM
|
|
||||||
- **UI:** Discord embeds and components
|
|
||||||
- **Validation:** Zod
|
|
||||||
- **Testing:** Bun Test
|
|
||||||
- **Container:** Docker
|
|
||||||
|
|
||||||
## Key Files Reference
|
|
||||||
|
|
||||||
| Purpose | File |
|
|
||||||
| ------------- | ---------------------- |
|
|
||||||
| Bot entry | `bot/index.ts` |
|
|
||||||
| DB schema | `shared/db/schema.ts` |
|
|
||||||
| Error classes | `shared/lib/errors.ts` |
|
|
||||||
| Config loader | `shared/lib/config.ts` |
|
|
||||||
| Environment | `shared/lib/env.ts` |
|
|
||||||
| Embed helpers | `bot/lib/embeds.ts` |
|
|
||||||
| Command utils | `shared/lib/utils.ts` |
|
|
||||||
| Error handler | `bot/lib/commandUtils.ts` |
|
|
||||||
|
|||||||
211
CLAUDE.md
@@ -1,211 +0,0 @@
|
|||||||
# CLAUDE.md
|
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
||||||
|
|
||||||
## Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Development
|
|
||||||
bun --watch bot/index.ts # Run bot + API with hot reload
|
|
||||||
docker compose up # Start all services (bot, API, database)
|
|
||||||
docker compose up app # Start just the app (bot + API)
|
|
||||||
docker compose up db # Start just the database
|
|
||||||
|
|
||||||
# Testing
|
|
||||||
bun test # Run all tests
|
|
||||||
bun test path/to/file.test.ts # Run a single test file
|
|
||||||
bun test shared/modules/economy # Run tests in a directory
|
|
||||||
bun test --watch # Watch mode
|
|
||||||
|
|
||||||
# Database
|
|
||||||
bun run db:push:local # Push schema changes (local)
|
|
||||||
bun run db:studio # Open Drizzle Studio (localhost:4983)
|
|
||||||
bun run generate # Generate Drizzle migrations (Docker)
|
|
||||||
bun run migrate # Apply migrations (Docker)
|
|
||||||
|
|
||||||
# Admin Panel
|
|
||||||
bun run panel:dev # Start Vite dev server for dashboard
|
|
||||||
bun run panel:build # Build React dashboard for production
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
Aurora is a Discord RPG bot + REST API running as a **single Bun process**. The bot and API share the same database client and services.
|
|
||||||
|
|
||||||
```
|
|
||||||
bot/ # Discord bot
|
|
||||||
├── commands/ # Slash commands by category (admin, economy, inventory, etc.)
|
|
||||||
├── events/ # Discord event handlers
|
|
||||||
├── lib/ # BotClient, handlers, loaders, embed helpers, commandUtils
|
|
||||||
├── modules/ # Feature modules (views, interactions per domain)
|
|
||||||
└── graphics/ # Canvas-based image generation (@napi-rs/canvas)
|
|
||||||
|
|
||||||
shared/ # Shared between bot and API
|
|
||||||
├── db/ # Drizzle ORM client + schema (users, economy, inventory, quests, etc.)
|
|
||||||
├── lib/ # env, config, errors, logger, types, utils
|
|
||||||
└── modules/ # Domain services (economy, user, inventory, quest, moderation, etc.)
|
|
||||||
|
|
||||||
api/ # REST API (Bun HTTP server)
|
|
||||||
└── src/routes/ # Route handlers for each domain
|
|
||||||
|
|
||||||
panel/ # React admin dashboard (Vite + Tailwind + Radix UI)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Key architectural details:**
|
|
||||||
- Bot and API both import from `shared/` — do not duplicate logic.
|
|
||||||
- Services in `shared/modules/` are singleton objects, not classes.
|
|
||||||
- The database uses PostgreSQL 16+ via Drizzle ORM with `bigint` mode for Discord IDs and currency.
|
|
||||||
- Feature modules follow a strict file suffix convention (see below).
|
|
||||||
|
|
||||||
## Import Conventions
|
|
||||||
|
|
||||||
Use path aliases (defined in `tsconfig.json`). Order: external packages → aliases → relative.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { SlashCommandBuilder } from "discord.js"; // external
|
|
||||||
import { economyService } from "@shared/modules/economy/economy.service"; // alias
|
|
||||||
import { users } from "@db/schema"; // alias
|
|
||||||
import { createErrorEmbed } from "@lib/embeds"; // alias
|
|
||||||
import { localHelper } from "./helper"; // relative
|
|
||||||
```
|
|
||||||
|
|
||||||
**Aliases:**
|
|
||||||
- `@/*` → `bot/`
|
|
||||||
- `@shared/*` → `shared/`
|
|
||||||
- `@db/*` → `shared/db/`
|
|
||||||
- `@lib/*` → `bot/lib/`
|
|
||||||
- `@modules/*` → `bot/modules/`
|
|
||||||
- `@commands/*` → `bot/commands/`
|
|
||||||
|
|
||||||
## Code Patterns
|
|
||||||
|
|
||||||
### Module File Suffixes
|
|
||||||
|
|
||||||
- `*.view.ts` — Creates Discord embeds/components
|
|
||||||
- `*.interaction.ts` — Handles button/select/modal interactions
|
|
||||||
- `*.service.ts` — Business logic (lives in `shared/modules/`)
|
|
||||||
- `*.types.ts` — Module-specific TypeScript types
|
|
||||||
- `*.test.ts` — Tests (co-located with source)
|
|
||||||
|
|
||||||
### Interaction Routing
|
|
||||||
|
|
||||||
Component interactions (buttons, select menus, modals) flow through a centralized routing system:
|
|
||||||
|
|
||||||
```
|
|
||||||
Discord event → interactionCreate → ComponentInteractionHandler → interaction.routes.ts → *.interaction.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
`ComponentInteractionHandler` (`bot/lib/handlers/ComponentInteractionHandler.ts`) iterates over the route table in `bot/lib/interaction.routes.ts`. Each route has a `predicate` that matches on `customId`, a lazy `handler` import, and a `method` name to call. The handler also provides centralized `UserError` / system error handling.
|
|
||||||
|
|
||||||
**Route table (custom ID prefix → handler):**
|
|
||||||
|
|
||||||
| Custom ID prefix | Handler file | Method |
|
|
||||||
| ------------------ | ----------------------------------------------- | ------------------------------ |
|
|
||||||
| `trade_`, `amount` | `bot/modules/trade/trade.interaction.ts` | `handleTradeInteraction` |
|
|
||||||
| `shop_buy_` | `bot/modules/economy/shop.interaction.ts` | `handleShopInteraction` |
|
|
||||||
| `lootdrop_` | `bot/modules/economy/lootdrop.interaction.ts` | `handleLootdropInteraction` |
|
|
||||||
| `trivia_` | `bot/modules/trivia/trivia.interaction.ts` | `handleTriviaInteraction` |
|
|
||||||
| `createitem_` | `bot/modules/admin/item_wizard.ts` | `handleItemWizardInteraction` |
|
|
||||||
| `enrollment` | `bot/modules/user/enrollment.interaction.ts` | `handleEnrollmentInteraction` |
|
|
||||||
| `feedback_` | `bot/modules/feedback/feedback.interaction.ts` | `handleFeedbackInteraction` |
|
|
||||||
|
|
||||||
Routes are evaluated in order — the first matching predicate wins. Some modules (e.g., inventory with `inv_` prefix) handle interactions locally via message component collectors instead of the global route table.
|
|
||||||
|
|
||||||
### Command Definition
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export const commandName = createCommand({
|
|
||||||
data: new SlashCommandBuilder().setName("name").setDescription("desc"),
|
|
||||||
execute: async (interaction) => {
|
|
||||||
await withCommandErrorHandling(interaction, async () => {
|
|
||||||
const result = await service.method();
|
|
||||||
await interaction.editReply({ embeds: [createSuccessEmbed(result)] });
|
|
||||||
}, { ephemeral: true });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
`withCommandErrorHandling` (from `@lib/commandUtils`) handles `deferReply`, `UserError` display, and unexpected error logging automatically.
|
|
||||||
|
|
||||||
### Service Pattern
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export const serviceName = {
|
|
||||||
methodName: async (params: ParamType): Promise<ReturnType> => {
|
|
||||||
return await withTransaction(async (tx) => {
|
|
||||||
// database operations
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { UserError, SystemError } from "@shared/lib/errors";
|
|
||||||
|
|
||||||
throw new UserError("You don't have enough coins!"); // shown to user
|
|
||||||
throw new SystemError("DB connection failed"); // logged, generic message shown
|
|
||||||
```
|
|
||||||
|
|
||||||
### Database Transactions
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { withTransaction } from "@/lib/db";
|
|
||||||
|
|
||||||
return await withTransaction(async (tx) => {
|
|
||||||
const user = await tx.query.users.findFirst({ where: eq(users.id, id) });
|
|
||||||
await tx.update(users).set({ coins: newBalance }).where(eq(users.id, id));
|
|
||||||
return user;
|
|
||||||
}, existingTx); // pass existing tx for nested transactions
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
Mock modules **before** imports. Use `bun:test`.
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { describe, it, expect, mock, beforeEach } from "bun:test";
|
|
||||||
|
|
||||||
mock.module("@shared/db/DrizzleClient", () => ({
|
|
||||||
DrizzleClient: { query: mockQuery },
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe("serviceName", () => {
|
|
||||||
beforeEach(() => mockFn.mockClear());
|
|
||||||
it("should handle expected case", async () => {
|
|
||||||
mockFn.mockResolvedValue(testData);
|
|
||||||
const result = await service.method(input);
|
|
||||||
expect(result).toEqual(expected);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Naming Conventions
|
|
||||||
|
|
||||||
| Element | Convention | Example |
|
|
||||||
| ---------------- | ---------------------- | -------------------------------- |
|
|
||||||
| Files | camelCase or kebab-case | `BotClient.ts`, `economy.service.ts` |
|
|
||||||
| Classes | PascalCase | `CommandHandler`, `UserError` |
|
|
||||||
| Functions | camelCase | `createCommand`, `handleShopInteraction` |
|
|
||||||
| Constants | UPPER_SNAKE_CASE | `EVENTS`, `BRANDING` |
|
|
||||||
| Enums | PascalCase | `TimerType`, `TransactionType` |
|
|
||||||
| Services | camelCase singleton | `economyService`, `userService` |
|
|
||||||
| Types/Interfaces | PascalCase | `Command`, `Event`, `GameConfigType` |
|
|
||||||
| DB tables | snake_case | `users`, `moderation_cases` |
|
|
||||||
| Custom IDs | snake_case with prefix | `shop_buy_`, `trade_accept_` |
|
|
||||||
| API routes | kebab-case | `/api/guild-settings` |
|
|
||||||
|
|
||||||
## Key Files
|
|
||||||
|
|
||||||
| Purpose | File |
|
|
||||||
| ----------------- | -------------------------- |
|
|
||||||
| Bot entry point | `bot/index.ts` |
|
|
||||||
| Discord client | `bot/lib/BotClient.ts` |
|
|
||||||
| DB schema index | `shared/db/schema.ts` |
|
|
||||||
| Error classes | `shared/lib/errors.ts` |
|
|
||||||
| Environment vars | `shared/lib/env.ts` |
|
|
||||||
| Config loader | `shared/lib/config.ts` |
|
|
||||||
| Embed helpers | `bot/lib/embeds.ts` |
|
|
||||||
| Command utils | `bot/lib/commandUtils.ts` |
|
|
||||||
| API server | `api/src/server.ts` |
|
|
||||||
270
README.md
@@ -1,159 +1,181 @@
|
|||||||
# Aurora
|
# Aurora
|
||||||
|
|
||||||
> A comprehensive, feature-rich Discord RPG bot built with modern technologies.
|
Aurora is a Discord RPG bot, admin/player panel, and REST/WebSocket API that run as one Bun application. The Discord bot and HTTP server share the same database client, config, services, and domain events.
|
||||||
|
|
||||||

|
## What exists today
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
Aurora is a powerful Discord bot designed to facilitate RPG-like elements within a Discord server. It features a robust economy, class system, inventory management, quests, and more, all built on top of a high-performance stack using Bun and Drizzle ORM.
|
- Discord slash commands for economy, inventory, quests, moderation, feedback, user profiles, and admin tooling.
|
||||||
|
- A Bun HTTP API under `/api/*`, Discord OAuth under `/auth/*`, and a WebSocket endpoint at `/ws`.
|
||||||
|
- A React panel for both admins and enrolled players.
|
||||||
|
- Shared domain services in `shared/modules/*` and reusable game plugins in `shared/games/*`.
|
||||||
|
- Built-in real-time games: chess and blackjack.
|
||||||
|
|
||||||
**New in v1.0:** Aurora now includes a fully integrated **REST API** for accessing bot data, statistics, and configuration, running alongside the bot in a single process.
|
## Architecture
|
||||||
|
|
||||||
## ✨ Features
|
```text
|
||||||
|
bot/ Discord bot entrypoint, commands, events, Discord-facing views/interactions
|
||||||
|
api/ Bun HTTP server, route modules, WebSocket/game room server
|
||||||
|
panel/ React 19 + Vite + Tailwind v4 dashboard
|
||||||
|
shared/ Shared DB schema, services, config, events, utilities, game plugins
|
||||||
|
docs/ Product and design notes
|
||||||
|
```
|
||||||
|
|
||||||
### Discord Bot
|
Important points:
|
||||||
* **Class System**: Users can join different classes.
|
|
||||||
* **Economy**: Complete economy system with balance, transactions, and daily rewards.
|
|
||||||
* **Inventory & Items**: Sophisticated item system with rarities, types (Material, Consumable, etc.), and inventory management.
|
|
||||||
* **Leveling**: XP-based leveling system to track user activity and progress.
|
|
||||||
* **Quests**: Quest system with requirements and rewards.
|
|
||||||
* **Trading**: Secure trading system between users.
|
|
||||||
* **Lootdrops**: Random loot drops in channels to engage users.
|
|
||||||
* **Admin Tools**: Administrative commands for server management.
|
|
||||||
|
|
||||||
### REST API
|
- `bot/index.ts` initializes DB-backed config, wires domain events, starts the API server, then logs into Discord.
|
||||||
* **Live Analytics**: Real-time statistics endpoint (commands, transactions).
|
- The API server also serves built panel assets from `panel/dist` when they exist.
|
||||||
* **Configuration Management**: Update bot settings via API.
|
- Bot commands, API routes, and the panel all rely on the same service layer in `shared/modules/*`.
|
||||||
* **Database Inspection**: Integrated Drizzle Studio access.
|
- Runtime game config is loaded from the `game_settings` table into `shared/lib/config.ts`.
|
||||||
* **WebSocket Support**: Real-time event streaming for live updates.
|
|
||||||
|
|
||||||
## 🏗️ Architecture
|
## Getting started
|
||||||
|
|
||||||
Aurora uses a **Single Process Monolith** architecture to maximize performance and simplify resource sharing.
|
|
||||||
|
|
||||||
* **Unified Runtime**: Both the Discord Client and the REST API run within the same Bun process.
|
|
||||||
* **Shared State**: This allows the API to access live bot memory (caches, gateways) directly without complex inter-process communication (IPC).
|
|
||||||
* **Simplified Deployment**: You only need to deploy a single Docker container.
|
|
||||||
|
|
||||||
## 🛠️ Tech Stack
|
|
||||||
|
|
||||||
* **Runtime**: [Bun](https://bun.sh/)
|
|
||||||
* **Bot Framework**: [Discord.js](https://discord.js.org/)
|
|
||||||
* **API Framework**: Bun HTTP Server (REST API)
|
|
||||||
* **UI**: Discord embeds and components
|
|
||||||
* **Database**: [PostgreSQL](https://www.postgresql.org/)
|
|
||||||
* **ORM**: [Drizzle ORM](https://orm.drizzle.team/)
|
|
||||||
* **Validation**: [Zod](https://zod.dev/)
|
|
||||||
* **Containerization**: [Docker](https://www.docker.com/)
|
|
||||||
|
|
||||||
## 🚀 Getting Started
|
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
* [Bun](https://bun.sh/) (latest version)
|
- Bun
|
||||||
* [Docker](https://www.docker.com/) & Docker Compose
|
- Docker and Docker Compose
|
||||||
|
- A Discord application with bot token, client ID, and client secret
|
||||||
|
|
||||||
### Installation
|
### Setup
|
||||||
|
|
||||||
1. **Clone the repository**
|
1. Install dependencies.
|
||||||
```bash
|
|
||||||
git clone <repository-url>
|
|
||||||
cd aurora
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Install dependencies**
|
```bash
|
||||||
```bash
|
bun install
|
||||||
bun install
|
```
|
||||||
```
|
|
||||||
|
|
||||||
3. **Environment Setup**
|
2. Create your environment file.
|
||||||
Copy the example environment file and configure it:
|
|
||||||
```bash
|
|
||||||
cp .env.example .env
|
|
||||||
```
|
|
||||||
Edit `.env` with your Discord bot token, Client ID, and database credentials.
|
|
||||||
|
|
||||||
> **Note**: The `DATABASE_URL` in `.env.example` is pre-configured for Docker.
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
4. **Start the Database**
|
3. Start PostgreSQL.
|
||||||
Run the database service using Docker Compose:
|
|
||||||
```bash
|
|
||||||
docker compose up -d db
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **Run Migrations**
|
```bash
|
||||||
```bash
|
docker compose up -d db
|
||||||
bun run migrate
|
```
|
||||||
```
|
|
||||||
OR
|
|
||||||
```bash
|
|
||||||
bun run db:push
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running the Bot & API
|
4. Initialize the schema.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run db:push:local
|
||||||
|
```
|
||||||
|
|
||||||
|
If you prefer running schema changes through Docker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Start the bot and API.
|
||||||
|
|
||||||
**Development Mode** (with hot reload):
|
|
||||||
```bash
|
```bash
|
||||||
bun run dev
|
bun run dev
|
||||||
```
|
```
|
||||||
* Bot: Online in Discord
|
|
||||||
* API: http://localhost:3000
|
|
||||||
|
|
||||||
**Production Mode**:
|
The Bun server listens on `http://localhost:3000`.
|
||||||
Build and run with Docker (recommended):
|
|
||||||
|
### Panel development
|
||||||
|
|
||||||
|
The Bun server can serve a built panel, but day-to-day panel work is done with Vite:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d app
|
bun run panel:dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### 🔐 Accessing Production Services (SSH Tunnel)
|
The panel dev server runs on `http://localhost:5173` and proxies `/api`, `/auth`, `/assets`, and `/ws` to `http://localhost:3000`.
|
||||||
|
|
||||||
For security, the Production Database and API are **not exposed** to the public internet by default. They are only accessible via localhost on the server.
|
To build the panel for the integrated Bun server:
|
||||||
|
|
||||||
To access them from your local machine, use the included SSH tunnel script.
|
```bash
|
||||||
|
bun run panel:build
|
||||||
1. Add your VPS details to your local `.env` file:
|
|
||||||
```env
|
|
||||||
VPS_USER=root
|
|
||||||
VPS_HOST=123.45.67.89
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Run the remote connection script:
|
|
||||||
```bash
|
|
||||||
bun run remote
|
|
||||||
```
|
|
||||||
|
|
||||||
This will establish secure tunnels for:
|
|
||||||
* **API**: http://localhost:3000
|
|
||||||
* **Drizzle Studio**: http://localhost:4983
|
|
||||||
|
|
||||||
## 📜 Scripts
|
|
||||||
|
|
||||||
* `bun run dev`: Start the bot and API server in watch mode.
|
|
||||||
* `bun run remote`: Open SSH tunnel to production services.
|
|
||||||
* `bun run generate`: Generate Drizzle migrations.
|
|
||||||
* `bun run migrate`: Apply migrations (via Docker).
|
|
||||||
* `bun run db:studio`: Open Drizzle Studio to inspect the database.
|
|
||||||
* `bun test`: Run tests.
|
|
||||||
|
|
||||||
## 📂 Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
├── bot # Discord Bot logic & entry point
|
|
||||||
├── web # REST API Server
|
|
||||||
├── shared # Shared code (Database, Config, Types)
|
|
||||||
├── drizzle # Drizzle migration files
|
|
||||||
├── scripts # Utility scripts
|
|
||||||
├── docker-compose.yml
|
|
||||||
└── package.json
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🤝 Contributing
|
## Useful scripts
|
||||||
|
|
||||||
Contributions are welcome! Please feel free to submit a Pull Request.
|
```bash
|
||||||
|
# App
|
||||||
|
bun run dev
|
||||||
|
docker compose up
|
||||||
|
docker compose up app
|
||||||
|
docker compose up db
|
||||||
|
|
||||||
## 📄 License
|
# Database
|
||||||
|
bun run db:push
|
||||||
|
bun run db:push:local
|
||||||
|
bun run db:generate
|
||||||
|
bun run db:migrate
|
||||||
|
bun run db:studio
|
||||||
|
bun run db:backup
|
||||||
|
bun run db:restore
|
||||||
|
|
||||||
This project is licensed under the MIT License.
|
# Panel
|
||||||
|
bun run panel:dev
|
||||||
|
bun run panel:build
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
bun test
|
||||||
|
bun run test
|
||||||
|
bun run test:ci
|
||||||
|
|
||||||
|
# Ops
|
||||||
|
bun run remote
|
||||||
|
bun run deploy
|
||||||
|
bun run deploy:remote
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment notes
|
||||||
|
|
||||||
|
The main variables you need in `.env` are:
|
||||||
|
|
||||||
|
- `DISCORD_BOT_TOKEN`
|
||||||
|
- `DISCORD_CLIENT_ID`
|
||||||
|
- `DISCORD_CLIENT_SECRET`
|
||||||
|
- `DISCORD_GUILD_ID`
|
||||||
|
- `ADMIN_USER_IDS`
|
||||||
|
- `SESSION_SECRET`
|
||||||
|
- `DB_USER`
|
||||||
|
- `DB_PASSWORD`
|
||||||
|
- `DB_NAME`
|
||||||
|
- `DATABASE_URL`
|
||||||
|
- `PANEL_BASE_URL`
|
||||||
|
|
||||||
|
Players can authenticate into the panel only after they exist in the `users` table. Admin access is determined by `ADMIN_USER_IDS`, and panel sessions are stored in signed cookies keyed by `SESSION_SECRET`.
|
||||||
|
|
||||||
|
## API and panel summary
|
||||||
|
|
||||||
|
- Public routes: `/auth/*`, `/api/health`
|
||||||
|
- Player-accessible API routes: `/api/stats`, `/api/health`, `/api/me`, `/api/me/inventory`
|
||||||
|
- Admin-only API routes: the rest of `/api/*`
|
||||||
|
- WebSocket: `/ws` with cookie-based auth
|
||||||
|
- Static assets: `/assets/*`
|
||||||
|
|
||||||
|
## Project structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
bot/
|
||||||
|
commands/
|
||||||
|
events/
|
||||||
|
lib/
|
||||||
|
modules/
|
||||||
|
|
||||||
|
api/
|
||||||
|
src/
|
||||||
|
routes/
|
||||||
|
games/
|
||||||
|
|
||||||
|
panel/
|
||||||
|
src/
|
||||||
|
|
||||||
|
shared/
|
||||||
|
db/
|
||||||
|
games/
|
||||||
|
lib/
|
||||||
|
modules/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [AGENTS.md](AGENTS.md): repo-wide implementation guidance
|
||||||
|
- [api/README.md](api/README.md): API surface and auth model
|
||||||
|
- [docs/new-design/DESIGN.md](docs/new-design/DESIGN.md): current panel design language
|
||||||
|
|||||||
132
api/README.md
@@ -1,30 +1,130 @@
|
|||||||
# Aurora Web API
|
# Aurora API
|
||||||
|
|
||||||
The web API provides a REST interface and WebSocket support for accessing Aurora bot data and configuration.
|
Aurora's API is a Bun server that runs inside the same process as the Discord bot. It serves REST routes, the authenticated WebSocket endpoint, static assets, and built panel files.
|
||||||
|
|
||||||
## API Endpoints
|
## Runtime model
|
||||||
|
|
||||||
- `GET /api/stats` - Real-time bot statistics
|
- Entry point: `api/src/server.ts`
|
||||||
- `GET /api/settings` - Bot configuration
|
- Route dispatcher: `api/src/routes/index.ts`
|
||||||
- `GET /api/users` - User data
|
- Auth: Discord OAuth with signed session cookies
|
||||||
- `GET /api/items` - Item catalog
|
- WebSocket: `/ws`
|
||||||
- `GET /api/quests` - Quest information
|
- Static assets: `/assets/*`
|
||||||
- `GET /api/transactions` - Economy data
|
- Built panel fallback: `panel/dist`
|
||||||
- `GET /api/health` - Health check
|
|
||||||
|
## Access model
|
||||||
|
|
||||||
|
Public:
|
||||||
|
|
||||||
|
- `GET /api/health`
|
||||||
|
- `/auth/discord`
|
||||||
|
- `/auth/callback`
|
||||||
|
- `POST /auth/logout`
|
||||||
|
- `GET /auth/me`
|
||||||
|
|
||||||
|
Player-accessible API routes:
|
||||||
|
|
||||||
|
- `GET /api/stats`
|
||||||
|
- `GET /api/health`
|
||||||
|
- `GET /api/me`
|
||||||
|
- `GET /api/me/inventory`
|
||||||
|
|
||||||
|
Admin-only API routes:
|
||||||
|
|
||||||
|
- everything else under `/api/*`
|
||||||
|
|
||||||
|
Admin vs player is derived from `ADMIN_USER_IDS`. A user must already exist in the `users` table to complete panel login.
|
||||||
|
|
||||||
|
## Route summary
|
||||||
|
|
||||||
|
### Auth
|
||||||
|
|
||||||
|
- `GET /auth/discord`
|
||||||
|
- `GET /auth/callback`
|
||||||
|
- `POST /auth/logout`
|
||||||
|
- `GET /auth/me`
|
||||||
|
|
||||||
|
### Dashboard and system
|
||||||
|
|
||||||
|
- `GET /api/health`
|
||||||
|
- `GET /api/stats`
|
||||||
|
- `GET /api/stats/activity`
|
||||||
|
- `POST /api/actions/reload-commands`
|
||||||
|
- `POST /api/actions/clear-cache`
|
||||||
|
- `POST /api/actions/maintenance-mode`
|
||||||
|
|
||||||
|
### Settings
|
||||||
|
|
||||||
|
- `GET /api/settings`
|
||||||
|
- `POST /api/settings`
|
||||||
|
- `GET /api/settings/meta`
|
||||||
|
- `GET /api/guilds/:guildId/settings`
|
||||||
|
- `PUT|PATCH /api/guilds/:guildId/settings`
|
||||||
|
- `DELETE /api/guilds/:guildId/settings`
|
||||||
|
|
||||||
|
### Users, classes, and inventory
|
||||||
|
|
||||||
|
- `GET /api/me`
|
||||||
|
- `GET /api/me/inventory`
|
||||||
|
- `GET /api/users`
|
||||||
|
- `GET /api/users/:id`
|
||||||
|
- `PUT /api/users/:id`
|
||||||
|
- `GET /api/users/:id/inventory`
|
||||||
|
- `POST /api/users/:id/inventory`
|
||||||
|
- `DELETE /api/users/:id/inventory/:itemId`
|
||||||
|
- `GET /api/classes`
|
||||||
|
- `POST /api/classes`
|
||||||
|
- `PUT /api/classes/:id`
|
||||||
|
- `DELETE /api/classes/:id`
|
||||||
|
|
||||||
|
### Game content
|
||||||
|
|
||||||
|
- `GET /api/items`
|
||||||
|
- `POST /api/items`
|
||||||
|
- `GET /api/items/:id`
|
||||||
|
- `PUT /api/items/:id`
|
||||||
|
- `DELETE /api/items/:id`
|
||||||
|
- `POST /api/items/:id/icon`
|
||||||
|
- `GET /api/quests`
|
||||||
|
- `POST /api/quests`
|
||||||
|
- `PUT /api/quests/:id`
|
||||||
|
- `DELETE /api/quests/:id`
|
||||||
|
- `GET /api/lootdrops`
|
||||||
|
- `POST /api/lootdrops`
|
||||||
|
- `DELETE /api/lootdrops/:messageId`
|
||||||
|
|
||||||
|
### Moderation and economy history
|
||||||
|
|
||||||
|
- `GET /api/moderation`
|
||||||
|
- `POST /api/moderation`
|
||||||
|
- `GET /api/transactions`
|
||||||
|
|
||||||
## WebSocket
|
## WebSocket
|
||||||
|
|
||||||
Connect to `/ws` for real-time updates:
|
`/ws` requires a valid `aurora_session` cookie.
|
||||||
- Stats broadcasts every 5 seconds
|
|
||||||
- Event notifications via system bus
|
Current behavior:
|
||||||
- PING/PONG heartbeat support
|
|
||||||
|
- dashboard clients subscribe to `dashboard`
|
||||||
|
- game clients also use lobby and room-scoped traffic through `GameServer`
|
||||||
|
- `PING` from the client returns `PONG`
|
||||||
|
- dashboard stats are broadcast every 5 seconds while at least one client is connected
|
||||||
|
- hard limits in `api/src/server.ts`:
|
||||||
|
- 200 concurrent connections
|
||||||
|
- 16 KB max payload
|
||||||
|
- 60 second idle timeout
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
The API runs automatically when you start the bot:
|
Start the backend:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run dev
|
bun run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
The API will be available at `http://localhost:3000`
|
Optional panel dev server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run panel:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Panel dev runs on `http://localhost:5173` and proxies API/auth/assets/WebSocket requests to `http://localhost:3000`.
|
||||||
|
|||||||
68
api/src/AGENTS.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# API layer
|
||||||
|
|
||||||
|
## Server shape
|
||||||
|
|
||||||
|
- Aurora uses Bun's native `serve()` API in `api/src/server.ts`.
|
||||||
|
- Route modules are aggregated in `api/src/routes/index.ts`.
|
||||||
|
- A route module returns `null` when it does not match so the dispatcher can continue.
|
||||||
|
- After route handling, the server tries `panel/dist` for SPA/static files.
|
||||||
|
|
||||||
|
## Authentication and authorization
|
||||||
|
|
||||||
|
- OAuth routes live in `api/src/routes/auth.routes.ts`.
|
||||||
|
- Sessions are stored in signed `aurora_session` cookies.
|
||||||
|
- Session TTL is 7 days.
|
||||||
|
- Login succeeds only for users already present in the `users` table.
|
||||||
|
- Role is `admin` if the Discord ID is in `ADMIN_USER_IDS`, otherwise `player`.
|
||||||
|
- Redirects after login are intentionally restricted to localhost or relative paths.
|
||||||
|
|
||||||
|
Current access rules from `api/src/routes/index.ts`:
|
||||||
|
|
||||||
|
- public: `/auth/*`, `/api/health`
|
||||||
|
- player allow-list: `/api/stats`, `/api/health`, `/api/me`
|
||||||
|
- everything else under `/api/*`: admin-only
|
||||||
|
|
||||||
|
`/api/me/inventory` is handled by `users.routes.ts` and still depends on a valid session.
|
||||||
|
|
||||||
|
## Response conventions
|
||||||
|
|
||||||
|
- `jsonResponse()` serializes `bigint` values as strings.
|
||||||
|
- `errorResponse()` returns `{ error, details? }`.
|
||||||
|
- `parseBody()` and `parseQuery()` validate with Zod and return a `Response` on failure.
|
||||||
|
- The API does not use a framework-level middleware stack; each route handles its own parsing and branching.
|
||||||
|
|
||||||
|
## WebSocket
|
||||||
|
|
||||||
|
- Endpoint: `/ws`
|
||||||
|
- Requires an authenticated session
|
||||||
|
- Dashboard channel: `dashboard`
|
||||||
|
- Lobby channel: `lobby`
|
||||||
|
- Room-specific messaging is handled inside `GameServer`
|
||||||
|
- Dashboard broadcasts `STATS_UPDATE` every 5 seconds while clients are connected
|
||||||
|
- `NEW_EVENT` broadcasts are wired from `shared/lib/events`
|
||||||
|
|
||||||
|
Hard limits:
|
||||||
|
|
||||||
|
- max connections: 200
|
||||||
|
- max payload: 16 KB
|
||||||
|
- idle timeout: 60 seconds
|
||||||
|
|
||||||
|
## Static files
|
||||||
|
|
||||||
|
- Built panel assets are served from `panel/dist`
|
||||||
|
- `/assets/*` serves files from `bot/assets/graphics`
|
||||||
|
- `/api/*`, `/auth/*`, `/ws`, and `/assets/*` bypass the SPA fallback
|
||||||
|
|
||||||
|
## Route notes
|
||||||
|
|
||||||
|
- `items.routes.ts` supports both JSON and multipart form data for item creation.
|
||||||
|
- `settings.routes.ts` writes DB-backed game settings and emits the reload-commands event.
|
||||||
|
- `guild-settings.routes.ts` invalidates the guild config cache after writes.
|
||||||
|
- `lootdrops.routes.ts` delegates spawning/deletion to bot-side handlers because Discord message creation happens there.
|
||||||
|
|
||||||
|
## Gotchas
|
||||||
|
|
||||||
|
- Some runtime caches are in-memory only and are lost on restart.
|
||||||
|
- The server registers game plugins at startup; duplicate registration throws.
|
||||||
|
- BigInt-safe JSON matters for nearly every domain route.
|
||||||
|
- The panel's auth flow depends on `PANEL_BASE_URL` matching the OAuth callback origin.
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# API Layer
|
|
||||||
|
|
||||||
## Server
|
|
||||||
- Bun's native `serve()` API — no Express/Fastify. Custom `handleRequest()` dispatcher with pathname prefix matching.
|
|
||||||
- Route modules export `{ name: string, handler: RouteHandler }`. Handlers return `null` for non-matching paths.
|
|
||||||
|
|
||||||
## Authentication
|
|
||||||
- Discord OAuth2 with session cookies (`aurora_session`, HttpOnly, 7-day TTL).
|
|
||||||
- In-memory session store — sessions lost on restart.
|
|
||||||
- Role-based: `admin` vs `player` (admins set via `ADMIN_USER_IDS` env var).
|
|
||||||
- Non-enrolled users (not in DB) get 403 even with valid Discord auth.
|
|
||||||
- Call `getSession(req)` for all protected routes.
|
|
||||||
|
|
||||||
## Response Conventions
|
|
||||||
- Success: `jsonResponse(data, status)` — uses custom BigInt-safe JSON replacer.
|
|
||||||
- Error: `errorResponse(message, status, details?)` → `{ error, details? }`
|
|
||||||
- Validation: `validationErrorResponse(zodError)` → `{ error: "Invalid payload", issues: [...] }`
|
|
||||||
- Zod schemas centralized in `schemas.ts`.
|
|
||||||
|
|
||||||
## WebSocket
|
|
||||||
- Upgrade via `/ws` endpoint (requires auth).
|
|
||||||
- Pub/sub via Bun's `.publish()` / `.subscribe()` on channels: `dashboard`, `lobby`, `room:${roomId}`.
|
|
||||||
- Dashboard stats broadcast every 5 seconds. Game events are room-scoped.
|
|
||||||
- Hard limit: 200 concurrent WS connections (429 rejection), 16KB max payload, 60s idle timeout.
|
|
||||||
- Fire-and-forget broadcasts — no ack mechanism.
|
|
||||||
|
|
||||||
## Gotchas
|
|
||||||
- All DB IDs are BigInt — JSON responses must use the custom `jsonReplacer`.
|
|
||||||
- No rate limiting on HTTP routes.
|
|
||||||
- Some routes accept multipart form data (e.g., item icon upload) — manual parsing, not abstracted.
|
|
||||||
- Asset directories resolve relative to `import.meta.dir`.
|
|
||||||
@@ -2,6 +2,9 @@ import { RoomManager } from "./RoomManager";
|
|||||||
import { GameWsClientSchema } from "./types";
|
import { GameWsClientSchema } from "./types";
|
||||||
import type { PlayerInfo } from "./types";
|
import type { PlayerInfo } from "./types";
|
||||||
import { logger } from "@shared/lib/logger";
|
import { logger } from "@shared/lib/logger";
|
||||||
|
import { economyService } from "@shared/modules/economy/economy.service";
|
||||||
|
import { TransactionType } from "@shared/lib/constants";
|
||||||
|
import { gameRegistry } from "@shared/games/registry";
|
||||||
import type { Server, ServerWebSocket } from "bun";
|
import type { Server, ServerWebSocket } from "bun";
|
||||||
|
|
||||||
export interface WsConnectionData {
|
export interface WsConnectionData {
|
||||||
@@ -13,7 +16,7 @@ export class GameServer {
|
|||||||
readonly roomManager = new RoomManager();
|
readonly roomManager = new RoomManager();
|
||||||
private connections = new Map<string, ServerWebSocket<WsConnectionData>>();
|
private connections = new Map<string, ServerWebSocket<WsConnectionData>>();
|
||||||
private replacedConnections = new Map<string, ServerWebSocket<WsConnectionData>>();
|
private replacedConnections = new Map<string, ServerWebSocket<WsConnectionData>>();
|
||||||
private bunServer: Server | null = null;
|
private bunServer: Server<WsConnectionData> | null = null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Subscribe to room events and route them to the right clients
|
// Subscribe to room events and route them to the right clients
|
||||||
@@ -57,7 +60,25 @@ export class GameServer {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.roomManager.emitter.on("game:ended", ({ roomId, winner, reason }) => {
|
this.roomManager.emitter.on("game:ended", ({ roomId, winner, reason, payouts }) => {
|
||||||
|
const room = this.roomManager.getRoom(roomId);
|
||||||
|
const betAmount = room?.betAmount ?? 0;
|
||||||
|
|
||||||
|
// Handle bet payouts asynchronously — broadcast happens after settlement
|
||||||
|
if (betAmount > 0) {
|
||||||
|
this.settleBets(roomId, winner, betAmount, payouts).then((payout) => {
|
||||||
|
this.publish(`room:${roomId}`, {
|
||||||
|
type: "GAME_ENDED",
|
||||||
|
roomId,
|
||||||
|
winner,
|
||||||
|
reason,
|
||||||
|
payout,
|
||||||
|
});
|
||||||
|
this.publishRoomListUpdate();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.publish(`room:${roomId}`, {
|
this.publish(`room:${roomId}`, {
|
||||||
type: "GAME_ENDED",
|
type: "GAME_ENDED",
|
||||||
roomId,
|
roomId,
|
||||||
@@ -67,6 +88,33 @@ export class GameServer {
|
|||||||
this.publishRoomListUpdate();
|
this.publishRoomListUpdate();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.roomManager.emitter.on("round:settled", async ({ roomId, roundSettlements }) => {
|
||||||
|
const room = this.roomManager.getRoom(roomId);
|
||||||
|
if (!room || room.betAmount <= 0) return;
|
||||||
|
const gameName = gameRegistry.get(room.gameSlug)?.name ?? "Game";
|
||||||
|
const settlementDetails: typeof roundSettlements = {};
|
||||||
|
|
||||||
|
for (const [playerId, settlement] of Object.entries(roundSettlements)) {
|
||||||
|
try {
|
||||||
|
if (settlement.payout > 0) {
|
||||||
|
await economyService.modifyUserBalance(
|
||||||
|
playerId,
|
||||||
|
BigInt(settlement.payout),
|
||||||
|
TransactionType.GAME_WIN,
|
||||||
|
`${gameName} round payout (room ${roomId.slice(0, 8)})`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
settlementDetails[playerId] = settlement;
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("web", `Round payout failed for ${playerId} in room ${roomId}: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(settlementDetails).length > 0) {
|
||||||
|
this.publish(`room:${roomId}`, { type: "ROUND_SETTLED", roomId, settlements: settlementDetails });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.roomManager.emitter.on("player:left", ({ roomId, playerId }) => {
|
this.roomManager.emitter.on("player:left", ({ roomId, playerId }) => {
|
||||||
this.publish(`room:${roomId}`, {
|
this.publish(`room:${roomId}`, {
|
||||||
type: "PLAYER_LEFT",
|
type: "PLAYER_LEFT",
|
||||||
@@ -91,7 +139,7 @@ export class GameServer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setServer(server: Server): void {
|
setServer(server: Server<WsConnectionData>): void {
|
||||||
this.bunServer = server;
|
this.bunServer = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +153,7 @@ export class GameServer {
|
|||||||
ws.send(JSON.stringify({ type: "ROOM_LIST_UPDATE", rooms: this.roomManager.listRooms() }));
|
ws.send(JSON.stringify({ type: "ROOM_LIST_UPDATE", rooms: this.roomManager.listRooms() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessage(ws: ServerWebSocket<WsConnectionData>, raw: unknown): void {
|
async handleMessage(ws: ServerWebSocket<WsConnectionData>, raw: unknown): Promise<void> {
|
||||||
const parsed = GameWsClientSchema.safeParse(raw);
|
const parsed = GameWsClientSchema.safeParse(raw);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
ws.send(JSON.stringify({ type: "ERROR", message: "Invalid message format" }));
|
ws.send(JSON.stringify({ type: "ERROR", message: "Invalid message format" }));
|
||||||
@@ -117,7 +165,11 @@ export class GameServer {
|
|||||||
|
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case "CREATE_ROOM": {
|
case "CREATE_ROOM": {
|
||||||
const result = this.roomManager.createRoom(msg.gameType, discordId);
|
// Solo mode forces betAmount to 0
|
||||||
|
const options = msg.options ? { ...msg.options } : {};
|
||||||
|
if (options.soloMode) options.betAmount = 0;
|
||||||
|
|
||||||
|
const result = this.roomManager.createRoom(msg.gameType, discordId, options);
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
ws.send(JSON.stringify({ type: "ERROR", message: result.error }));
|
ws.send(JSON.stringify({ type: "ERROR", message: result.error }));
|
||||||
return;
|
return;
|
||||||
@@ -126,6 +178,26 @@ export class GameServer {
|
|||||||
ws.data.rooms.add(result.roomId);
|
ws.data.rooms.add(result.roomId);
|
||||||
ws.send(JSON.stringify({ type: "ROOM_CREATED", roomId: result.roomId, gameSlug: msg.gameType }));
|
ws.send(JSON.stringify({ type: "ROOM_CREATED", roomId: result.roomId, gameSlug: msg.gameType }));
|
||||||
logger.info("web", `Room created: ${result.roomId} (${msg.gameType}) by ${discordId}`);
|
logger.info("web", `Room created: ${result.roomId} (${msg.gameType}) by ${discordId}`);
|
||||||
|
|
||||||
|
// Solo mode: auto-fill and start immediately
|
||||||
|
if (options.soloMode) {
|
||||||
|
const fillResult = this.roomManager.fillRoom(result.roomId, discordId);
|
||||||
|
if (!fillResult.ok) {
|
||||||
|
ws.send(JSON.stringify({ type: "ERROR", message: fillResult.error }));
|
||||||
|
}
|
||||||
|
// fillRoom with betAmount=0 calls startGame internally
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-start if room is immediately full (e.g. maxPlayers: 1) — skip for manualStart games
|
||||||
|
const plugin = gameRegistry.get(msg.gameType);
|
||||||
|
const createdRoom = this.roomManager.getRoom(result.roomId);
|
||||||
|
if (!options.soloMode && plugin && !plugin.manualStart && createdRoom && createdRoom.players.length >= plugin.maxPlayers && createdRoom.status === "waiting") {
|
||||||
|
if (createdRoom.betAmount > 0) {
|
||||||
|
this.deductBetsAndStart(result.roomId, createdRoom.betAmount, createdRoom.players, ws);
|
||||||
|
} else {
|
||||||
|
this.roomManager.startGame(result.roomId);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +241,14 @@ export class GameServer {
|
|||||||
}
|
}
|
||||||
this.replacedConnections.delete(discordId);
|
this.replacedConnections.delete(discordId);
|
||||||
|
|
||||||
|
// Build room options for the client
|
||||||
|
const roomOptions = room
|
||||||
|
? {
|
||||||
|
...(room.betAmount > 0 ? { betAmount: room.betAmount } : {}),
|
||||||
|
...(typeof room.options?.timeControl === "string" ? { timeControl: room.options.timeControl } : {}),
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
// Respond with JOIN_RESULT
|
// Respond with JOIN_RESULT
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
type: "JOIN_RESULT",
|
type: "JOIN_RESULT",
|
||||||
@@ -178,6 +258,7 @@ export class GameServer {
|
|||||||
players,
|
players,
|
||||||
spectators,
|
spectators,
|
||||||
state,
|
state,
|
||||||
|
roomOptions,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Notify other room members
|
// Notify other room members
|
||||||
@@ -190,6 +271,11 @@ export class GameServer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
logger.info("web", `${discordId} joined room ${msg.roomId} as ${result.joinedAs}`);
|
logger.info("web", `${discordId} joined room ${msg.roomId} as ${result.joinedAs}`);
|
||||||
|
|
||||||
|
// Handle async bet deduction when room is ready to start
|
||||||
|
if (result.readyToStart && room) {
|
||||||
|
this.deductBetsAndStart(msg.roomId, room.betAmount, room.players, ws);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,6 +287,30 @@ export class GameServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "GAME_ACTION": {
|
case "GAME_ACTION": {
|
||||||
|
// Action cost pre-check: deduct bet before processing split/double/place_bet
|
||||||
|
const actionRoom = this.roomManager.getRoom(msg.roomId);
|
||||||
|
if (actionRoom && actionRoom.betAmount > 0 && actionRoom.state) {
|
||||||
|
const actionPlugin = gameRegistry.get(actionRoom.gameSlug);
|
||||||
|
if (actionPlugin?.getActionCost) {
|
||||||
|
const cost = actionPlugin.getActionCost(actionRoom.state, msg.action, discordId);
|
||||||
|
if (cost > 0) {
|
||||||
|
const amount = actionRoom.betAmount * cost;
|
||||||
|
const gameName = actionPlugin.name ?? actionRoom.gameSlug;
|
||||||
|
try {
|
||||||
|
await economyService.modifyUserBalance(
|
||||||
|
discordId,
|
||||||
|
-BigInt(amount),
|
||||||
|
TransactionType.GAME_BET,
|
||||||
|
`${gameName} action bet (room ${msg.roomId.slice(0, 8)})`,
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
ws.send(JSON.stringify({ type: "ERROR", message: "Insufficient funds for this action" }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const result = this.roomManager.handleAction(msg.roomId, discordId, msg.action);
|
const result = this.roomManager.handleAction(msg.roomId, discordId, msg.action);
|
||||||
if (!result.ok) {
|
if (!result.ok) {
|
||||||
ws.send(JSON.stringify({ type: "ERROR", message: result.error }));
|
ws.send(JSON.stringify({ type: "ERROR", message: result.error }));
|
||||||
@@ -210,16 +320,51 @@ export class GameServer {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "START_GAME": {
|
||||||
|
const room = this.roomManager.getRoom(msg.roomId);
|
||||||
|
if (!room) {
|
||||||
|
ws.send(JSON.stringify({ type: "ERROR", message: "Room not found" }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (room.host !== discordId) {
|
||||||
|
ws.send(JSON.stringify({ type: "ERROR", message: "Only the host can start the game" }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (room.status !== "waiting") {
|
||||||
|
ws.send(JSON.stringify({ type: "ERROR", message: "Game is not in waiting state" }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const startPlugin = gameRegistry.get(room.gameSlug);
|
||||||
|
if (startPlugin && room.players.length < startPlugin.minPlayers) {
|
||||||
|
ws.send(JSON.stringify({ type: "ERROR", message: `Need at least ${startPlugin.minPlayers} player(s) to start` }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (room.betAmount > 0) {
|
||||||
|
this.deductBetsAndStart(msg.roomId, room.betAmount, room.players, ws);
|
||||||
|
} else {
|
||||||
|
const startResult = this.roomManager.startGame(msg.roomId);
|
||||||
|
if (!startResult.ok) {
|
||||||
|
ws.send(JSON.stringify({ type: "ERROR", message: startResult.error }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info("web", `Host ${discordId} started game in room ${msg.roomId}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "FILL_ROOM": {
|
case "FILL_ROOM": {
|
||||||
if (role !== "admin") {
|
if (role !== "admin") {
|
||||||
ws.send(JSON.stringify({ type: "ERROR", message: "Only admins can fill a room for solo testing" }));
|
ws.send(JSON.stringify({ type: "ERROR", message: "Only admins can fill a room for solo testing" }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = this.roomManager.fillRoom(msg.roomId, discordId);
|
const fillResult = this.roomManager.fillRoom(msg.roomId, discordId);
|
||||||
if (!result.ok) {
|
if (!fillResult.ok) {
|
||||||
ws.send(JSON.stringify({ type: "ERROR", message: result.error }));
|
ws.send(JSON.stringify({ type: "ERROR", message: fillResult.error }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (fillResult.readyToStart) {
|
||||||
|
const room = this.roomManager.getRoom(msg.roomId);
|
||||||
|
if (room) this.deductBetsAndStart(msg.roomId, room.betAmount, room.players, ws);
|
||||||
|
}
|
||||||
logger.info("web", `Admin ${discordId} filled room ${msg.roomId} for solo testing`);
|
logger.info("web", `Admin ${discordId} filled room ${msg.roomId} for solo testing`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -241,6 +386,144 @@ export class GameServer {
|
|||||||
this.connections.delete(ws.data.session.discordId);
|
this.connections.delete(ws.data.session.discordId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deduct bet amounts from all players, then start the game.
|
||||||
|
* If any player can't afford the bet, refund already-deducted players
|
||||||
|
* and remove the failing player from the room.
|
||||||
|
*/
|
||||||
|
private async deductBetsAndStart(
|
||||||
|
roomId: string,
|
||||||
|
betAmount: number,
|
||||||
|
playerIds: string[],
|
||||||
|
triggeringWs: ServerWebSocket<WsConnectionData>,
|
||||||
|
): Promise<void> {
|
||||||
|
const room = this.roomManager.getRoom(roomId);
|
||||||
|
if (!room || room.betsPending) return;
|
||||||
|
|
||||||
|
// Games with getActionCost handle per-round betting themselves (e.g., blackjack).
|
||||||
|
// Skip the upfront deduction — just start the game.
|
||||||
|
const plugin = gameRegistry.get(room.gameSlug);
|
||||||
|
if (plugin?.getActionCost) {
|
||||||
|
const startResult = this.roomManager.startGame(roomId);
|
||||||
|
if (!startResult.ok) {
|
||||||
|
triggeringWs.send(JSON.stringify({ type: "ERROR", message: startResult.error }));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
room.betsPending = true;
|
||||||
|
|
||||||
|
const uniquePlayers = [...new Set(playerIds)];
|
||||||
|
const deducted: string[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const gameName = gameRegistry.get(room.gameSlug)?.name ?? room.gameSlug;
|
||||||
|
for (const pid of uniquePlayers) {
|
||||||
|
await economyService.modifyUserBalance(
|
||||||
|
pid,
|
||||||
|
-BigInt(betAmount),
|
||||||
|
TransactionType.GAME_BET,
|
||||||
|
`${gameName} wager (room ${roomId.slice(0, 8)})`,
|
||||||
|
);
|
||||||
|
deducted.push(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// All deductions succeeded — start the game
|
||||||
|
const startResult = this.roomManager.startGame(roomId);
|
||||||
|
if (!startResult.ok) {
|
||||||
|
// Shouldn't happen, but refund if it does
|
||||||
|
await this.refundPlayers(deducted, betAmount, roomId);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Refund anyone already deducted
|
||||||
|
await this.refundPlayers(deducted, betAmount, roomId);
|
||||||
|
|
||||||
|
// Find the player who couldn't afford the bet
|
||||||
|
const failedPlayer = uniquePlayers.find(p => !deducted.includes(p));
|
||||||
|
if (failedPlayer) {
|
||||||
|
this.roomManager.removePlayer(roomId, failedPlayer);
|
||||||
|
this.sendToPlayer(failedPlayer, {
|
||||||
|
type: "ERROR",
|
||||||
|
message: "Insufficient funds for the bet. You have been removed from the room.",
|
||||||
|
});
|
||||||
|
this.publish(`room:${roomId}`, {
|
||||||
|
type: "PLAYER_LEFT",
|
||||||
|
roomId,
|
||||||
|
playerId: failedPlayer,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.warn("web", `Bet deduction failed for room ${roomId}: ${err}`);
|
||||||
|
} finally {
|
||||||
|
if (room) room.betsPending = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Pay out winnings or refund bets on game end. */
|
||||||
|
private async settleBets(
|
||||||
|
roomId: string,
|
||||||
|
winner: string | null,
|
||||||
|
betAmount: number,
|
||||||
|
payouts?: Record<string, number>,
|
||||||
|
): Promise<{ amount: number; refunded?: boolean }> {
|
||||||
|
const room = this.roomManager.getRoom(roomId);
|
||||||
|
const uniquePlayers = [...new Set(room?.players ?? [])];
|
||||||
|
const gameName = gameRegistry.get(room?.gameSlug ?? "")?.name ?? "Game";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Custom payouts override default pot logic (used by house-edge games like blackjack)
|
||||||
|
if (payouts) {
|
||||||
|
let totalPaid = 0;
|
||||||
|
for (const [playerId, multiplier] of Object.entries(payouts)) {
|
||||||
|
if (multiplier <= 0) continue;
|
||||||
|
const amount = Math.floor(betAmount * multiplier);
|
||||||
|
await economyService.modifyUserBalance(
|
||||||
|
playerId,
|
||||||
|
BigInt(amount),
|
||||||
|
TransactionType.GAME_WIN,
|
||||||
|
`${gameName} payout (room ${roomId.slice(0, 8)})`,
|
||||||
|
);
|
||||||
|
totalPaid = Math.max(totalPaid, amount);
|
||||||
|
}
|
||||||
|
const isRefund = !winner && totalPaid === betAmount;
|
||||||
|
return { amount: totalPaid, refunded: isRefund };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default pot logic: winner takes all, draw refunds everyone
|
||||||
|
const pot = betAmount * uniquePlayers.length;
|
||||||
|
if (winner) {
|
||||||
|
await economyService.modifyUserBalance(
|
||||||
|
winner,
|
||||||
|
BigInt(pot),
|
||||||
|
TransactionType.GAME_WIN,
|
||||||
|
`${gameName} wager won (room ${roomId.slice(0, 8)})`,
|
||||||
|
);
|
||||||
|
return { amount: pot };
|
||||||
|
} else {
|
||||||
|
await this.refundPlayers(uniquePlayers, betAmount, roomId, gameName);
|
||||||
|
return { amount: betAmount, refunded: true };
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("web", `Bet settlement failed for room ${roomId}: ${err}`);
|
||||||
|
return { amount: 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async refundPlayers(playerIds: string[], betAmount: number, roomId: string, gameName = "Game"): Promise<void> {
|
||||||
|
for (const pid of playerIds) {
|
||||||
|
try {
|
||||||
|
await economyService.modifyUserBalance(
|
||||||
|
pid,
|
||||||
|
BigInt(betAmount),
|
||||||
|
TransactionType.GAME_WIN,
|
||||||
|
`${gameName} wager refund (room ${roomId.slice(0, 8)})`,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("web", `Failed to refund ${pid} for room ${roomId}: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private publish(channel: string, message: unknown): void {
|
private publish(channel: string, message: unknown): void {
|
||||||
this.bunServer?.publish(channel, JSON.stringify(message));
|
this.bunServer?.publish(channel, JSON.stringify(message));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,4 +154,34 @@ describe("RoomManager", () => {
|
|||||||
expect(empty.length).toBe(0);
|
expect(empty.length).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("waiting room cleanup", () => {
|
||||||
|
it("should remove waiting rooms after the configured timeout", async () => {
|
||||||
|
const shortLivedManager = new RoomManager({ WAITING_CLEANUP_MS: 20 });
|
||||||
|
const create = shortLivedManager.createRoom("stub", "player1");
|
||||||
|
if (!create.ok) throw new Error("Failed to create room");
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 35));
|
||||||
|
|
||||||
|
expect(shortLivedManager.getRoom(create.roomId)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should refresh the waiting room timeout when the room is active", async () => {
|
||||||
|
const shortLivedManager = new RoomManager({ WAITING_CLEANUP_MS: 25 });
|
||||||
|
const create = shortLivedManager.createRoom("stub", "player1");
|
||||||
|
if (!create.ok) throw new Error("Failed to create room");
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 15));
|
||||||
|
|
||||||
|
const spectatorJoin = shortLivedManager.joinRoom(create.roomId, "spectator1", "spectator");
|
||||||
|
expect(spectatorJoin.ok).toBe(true);
|
||||||
|
expect(shortLivedManager.getRoom(create.roomId)).toBeDefined();
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 15));
|
||||||
|
expect(shortLivedManager.getRoom(create.roomId)).toBeDefined();
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 20));
|
||||||
|
expect(shortLivedManager.getRoom(create.roomId)).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,28 +1,33 @@
|
|||||||
import mitt from "mitt";
|
import mitt from "mitt";
|
||||||
import { gameRegistry } from "@shared/games/registry";
|
import { gameRegistry } from "@shared/games/registry";
|
||||||
import type { Room, RoomSummary } from "./types";
|
import type { Room, RoomSummary } from "./types";
|
||||||
|
import type { RoundSettlement } from "@shared/games/types";
|
||||||
|
|
||||||
const ROOM_CONFIG = {
|
const DEFAULT_ROOM_CONFIG = {
|
||||||
WAITING_CLEANUP_MS: 60_000,
|
WAITING_CLEANUP_MS: 15 * 60_000,
|
||||||
FINISHED_CLEANUP_MS: 60_000,
|
FINISHED_CLEANUP_MS: 60_000,
|
||||||
PLAYING_MAX_MS: 30 * 60_000, // 30 minutes — safety net for stuck games
|
PLAYING_MAX_MS: 30 * 60_000, // 30 minutes — safety net for stuck games
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
type RoomManagerConfig = typeof DEFAULT_ROOM_CONFIG;
|
||||||
|
|
||||||
type ActionResult =
|
type ActionResult =
|
||||||
| { ok: true; state: unknown; gameOver: { winner: string | null; reason: string } | null }
|
| { ok: true; state: unknown; gameOver: { winner: string | null; reason: string } | null; roundSettlements?: Record<string, RoundSettlement> }
|
||||||
| { ok: false; error: string };
|
| { ok: false; error: string };
|
||||||
|
|
||||||
type CreateResult = { ok: true; roomId: string } | { ok: false; error: string };
|
type CreateResult = { ok: true; roomId: string } | { ok: false; error: string };
|
||||||
type JoinResult =
|
type JoinResult =
|
||||||
| { ok: true; joinedAs: "player" | "spectator"; started: boolean }
|
| { ok: true; joinedAs: "player" | "spectator"; started: boolean; readyToStart?: boolean }
|
||||||
| { ok: false; error: string };
|
| { ok: false; error: string };
|
||||||
|
type FillResult = { ok: true; readyToStart?: boolean } | { ok: false; error: string };
|
||||||
|
|
||||||
type RoomEvents = {
|
type RoomEvents = {
|
||||||
"room:created": { roomId: string; gameSlug: string; hostId: string };
|
"room:created": { roomId: string; gameSlug: string; hostId: string };
|
||||||
"player:joined": { roomId: string; playerId: string; username: string; joinedAs: "player" | "spectator" };
|
"player:joined": { roomId: string; playerId: string; username: string; joinedAs: "player" | "spectator" };
|
||||||
"game:started": { roomId: string; spectatorView: unknown; playerViews: Map<string, unknown> };
|
"game:started": { roomId: string; spectatorView: unknown; playerViews: Map<string, unknown> };
|
||||||
"game:updated": { roomId: string; spectatorView: unknown; playerViews: Map<string, unknown> };
|
"game:updated": { roomId: string; spectatorView: unknown; playerViews: Map<string, unknown> };
|
||||||
"game:ended": { roomId: string; winner: string | null; reason: string };
|
"game:ended": { roomId: string; winner: string | null; reason: string; payouts?: Record<string, number> };
|
||||||
|
"round:settled": { roomId: string; roundSettlements: Record<string, RoundSettlement> };
|
||||||
"player:left": { roomId: string; playerId: string };
|
"player:left": { roomId: string; playerId: string };
|
||||||
"room:deleted": { roomId: string };
|
"room:deleted": { roomId: string };
|
||||||
"room:list:changed": void;
|
"room:list:changed": void;
|
||||||
@@ -31,13 +36,19 @@ type RoomEvents = {
|
|||||||
export class RoomManager {
|
export class RoomManager {
|
||||||
private rooms = new Map<string, Room>();
|
private rooms = new Map<string, Room>();
|
||||||
private cleanupTimers = new Map<string, Timer>();
|
private cleanupTimers = new Map<string, Timer>();
|
||||||
|
private readonly config: RoomManagerConfig;
|
||||||
readonly emitter = mitt<RoomEvents>();
|
readonly emitter = mitt<RoomEvents>();
|
||||||
|
|
||||||
createRoom(gameSlug: string, hostId: string): CreateResult {
|
constructor(config: Partial<RoomManagerConfig> = {}) {
|
||||||
|
this.config = { ...DEFAULT_ROOM_CONFIG, ...config };
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoom(gameSlug: string, hostId: string, options?: Record<string, unknown>): CreateResult {
|
||||||
const plugin = gameRegistry.get(gameSlug);
|
const plugin = gameRegistry.get(gameSlug);
|
||||||
if (!plugin) return { ok: false, error: `Unknown game type: ${gameSlug}` };
|
if (!plugin) return { ok: false, error: `Unknown game type: ${gameSlug}` };
|
||||||
|
|
||||||
const id = crypto.randomUUID();
|
const id = crypto.randomUUID();
|
||||||
|
const betAmount = typeof options?.betAmount === "number" && options.betAmount > 0 ? options.betAmount : 0;
|
||||||
const room: Room = {
|
const room: Room = {
|
||||||
id,
|
id,
|
||||||
gameSlug,
|
gameSlug,
|
||||||
@@ -47,10 +58,12 @@ export class RoomManager {
|
|||||||
state: null,
|
state: null,
|
||||||
status: "waiting",
|
status: "waiting",
|
||||||
createdAt: Date.now(),
|
createdAt: Date.now(),
|
||||||
|
options,
|
||||||
|
betAmount,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.rooms.set(id, room);
|
this.rooms.set(id, room);
|
||||||
this.scheduleCleanup(id, ROOM_CONFIG.WAITING_CLEANUP_MS);
|
this.refreshWaitingCleanup(id, room);
|
||||||
|
|
||||||
this.emitter.emit("room:created", { roomId: id, gameSlug, hostId });
|
this.emitter.emit("room:created", { roomId: id, gameSlug, hostId });
|
||||||
this.emitter.emit("room:list:changed");
|
this.emitter.emit("room:list:changed");
|
||||||
@@ -65,11 +78,13 @@ export class RoomManager {
|
|||||||
// Reconnecting player: must be checked before the in-progress spectator guard.
|
// Reconnecting player: must be checked before the in-progress spectator guard.
|
||||||
if (preferAs !== "spectator" && room.players.includes(playerId)) {
|
if (preferAs !== "spectator" && room.players.includes(playerId)) {
|
||||||
room.spectators.delete(playerId);
|
room.spectators.delete(playerId);
|
||||||
|
this.refreshWaitingCleanup(roomId, room);
|
||||||
return { ok: true, joinedAs: "player", started: room.status === "playing" };
|
return { ok: true, joinedAs: "player", started: room.status === "playing" };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preferAs === "spectator" || room.status !== "waiting") {
|
if (preferAs === "spectator" || room.status !== "waiting") {
|
||||||
room.spectators.add(playerId);
|
room.spectators.add(playerId);
|
||||||
|
this.refreshWaitingCleanup(roomId, room);
|
||||||
return { ok: true, joinedAs: "spectator", started: room.status === "playing" };
|
return { ok: true, joinedAs: "spectator", started: room.status === "playing" };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,21 +99,18 @@ export class RoomManager {
|
|||||||
|
|
||||||
room.players.push(playerId);
|
room.players.push(playerId);
|
||||||
|
|
||||||
if (room.players.length >= plugin.maxPlayers) {
|
if (room.players.length >= plugin.maxPlayers && !plugin.manualStart) {
|
||||||
room.state = plugin.createInitialState(room.players);
|
// Defer start when bets are involved — GameServer handles async deduction first
|
||||||
room.status = "playing";
|
if (room.betAmount > 0) {
|
||||||
this.scheduleCleanup(roomId, ROOM_CONFIG.PLAYING_MAX_MS);
|
this.refreshWaitingCleanup(roomId, room);
|
||||||
|
this.emitter.emit("room:list:changed");
|
||||||
const spectatorView = plugin.getSpectatorView(room.state);
|
return { ok: true, joinedAs: "player", started: false, readyToStart: true };
|
||||||
const playerViews = new Map<string, unknown>();
|
|
||||||
for (const pid of room.players) {
|
|
||||||
playerViews.set(pid, plugin.getPlayerView(room.state, pid));
|
|
||||||
}
|
}
|
||||||
this.emitter.emit("game:started", { roomId, spectatorView, playerViews });
|
this.startGame(roomId);
|
||||||
this.emitter.emit("room:list:changed");
|
|
||||||
return { ok: true, joinedAs: "player", started: true };
|
return { ok: true, joinedAs: "player", started: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.refreshWaitingCleanup(roomId, room);
|
||||||
this.emitter.emit("room:list:changed");
|
this.emitter.emit("room:list:changed");
|
||||||
return { ok: true, joinedAs: "player", started: false };
|
return { ok: true, joinedAs: "player", started: false };
|
||||||
}
|
}
|
||||||
@@ -107,9 +119,19 @@ export class RoomManager {
|
|||||||
const room = this.rooms.get(roomId);
|
const room = this.rooms.get(roomId);
|
||||||
if (!room) return { ok: false, error: "Room not found" };
|
if (!room) return { ok: false, error: "Room not found" };
|
||||||
if (room.status !== "playing") return { ok: false, error: "Game is not in progress" };
|
if (room.status !== "playing") return { ok: false, error: "Game is not in progress" };
|
||||||
if (!room.players.includes(playerId)) return { ok: false, error: "You are not a player in this game" };
|
|
||||||
|
|
||||||
const plugin = gameRegistry.get(room.gameSlug)!;
|
const plugin = gameRegistry.get(room.gameSlug)!;
|
||||||
|
|
||||||
|
// Spectator-to-player promotion for actions like "sit_down"
|
||||||
|
if (!room.players.includes(playerId)) {
|
||||||
|
if (room.spectators.has(playerId) && plugin.isSpectatorAction?.(action as any)) {
|
||||||
|
room.spectators.delete(playerId);
|
||||||
|
room.players.push(playerId);
|
||||||
|
} else {
|
||||||
|
return { ok: false, error: "You are not a player in this game" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const result = plugin.handleAction(room.state, action, playerId);
|
const result = plugin.handleAction(room.state, action, playerId);
|
||||||
if (!result.ok) return result;
|
if (!result.ok) return result;
|
||||||
|
|
||||||
@@ -117,22 +139,27 @@ export class RoomManager {
|
|||||||
const gameOver = plugin.isGameOver?.(room.state) ?? null;
|
const gameOver = plugin.isGameOver?.(room.state) ?? null;
|
||||||
if (gameOver) {
|
if (gameOver) {
|
||||||
room.status = "finished";
|
room.status = "finished";
|
||||||
this.scheduleCleanup(roomId, ROOM_CONFIG.FINISHED_CLEANUP_MS);
|
this.scheduleCleanup(roomId, this.config.FINISHED_CLEANUP_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
const spectatorView = plugin.getSpectatorView(room.state);
|
const spectatorView = plugin.getSpectatorView(room.state);
|
||||||
const playerViews = new Map<string, unknown>();
|
const playerViews = new Map<string, unknown>();
|
||||||
for (const pid of room.players) {
|
for (const pid of new Set(room.players)) {
|
||||||
playerViews.set(pid, plugin.getPlayerView(room.state, pid));
|
playerViews.set(pid, plugin.getPlayerView(room.state, pid));
|
||||||
}
|
}
|
||||||
this.emitter.emit("game:updated", { roomId, spectatorView, playerViews });
|
this.emitter.emit("game:updated", { roomId, spectatorView, playerViews });
|
||||||
|
|
||||||
|
// Emit round payouts for mid-game settlement (continuous-play games)
|
||||||
|
if (result.roundSettlements && !gameOver) {
|
||||||
|
this.emitter.emit("round:settled", { roomId, roundSettlements: result.roundSettlements });
|
||||||
|
}
|
||||||
|
|
||||||
if (gameOver) {
|
if (gameOver) {
|
||||||
this.emitter.emit("game:ended", { roomId, winner: gameOver.winner, reason: gameOver.reason });
|
this.emitter.emit("game:ended", { roomId, winner: gameOver.winner, reason: gameOver.reason, payouts: gameOver.payouts });
|
||||||
this.emitter.emit("room:list:changed");
|
this.emitter.emit("room:list:changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ok: true, state: room.state, gameOver };
|
return { ok: true, state: room.state, gameOver, roundSettlements: result.roundSettlements };
|
||||||
}
|
}
|
||||||
|
|
||||||
leaveRoom(roomId: string, playerId: string): void {
|
leaveRoom(roomId: string, playerId: string): void {
|
||||||
@@ -152,8 +179,8 @@ export class RoomManager {
|
|||||||
const gameOver = plugin.isGameOver?.(room.state) ?? null;
|
const gameOver = plugin.isGameOver?.(room.state) ?? null;
|
||||||
if (gameOver) {
|
if (gameOver) {
|
||||||
room.status = "finished";
|
room.status = "finished";
|
||||||
this.scheduleCleanup(roomId, ROOM_CONFIG.FINISHED_CLEANUP_MS);
|
this.scheduleCleanup(roomId, this.config.FINISHED_CLEANUP_MS);
|
||||||
this.emitter.emit("game:ended", { roomId, winner: gameOver.winner, reason: gameOver.reason });
|
this.emitter.emit("game:ended", { roomId, winner: gameOver.winner, reason: gameOver.reason, payouts: gameOver.payouts });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,6 +191,8 @@ export class RoomManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.refreshWaitingCleanup(roomId, room);
|
||||||
|
|
||||||
this.emitter.emit("player:left", { roomId, playerId });
|
this.emitter.emit("player:left", { roomId, playerId });
|
||||||
this.emitter.emit("room:list:changed");
|
this.emitter.emit("room:list:changed");
|
||||||
}
|
}
|
||||||
@@ -174,7 +203,7 @@ export class RoomManager {
|
|||||||
* (e.g. ["alice", "alice"]). Plugin authors should be aware that
|
* (e.g. ["alice", "alice"]). Plugin authors should be aware that
|
||||||
* solo-test mode produces non-unique player arrays.
|
* solo-test mode produces non-unique player arrays.
|
||||||
*/
|
*/
|
||||||
fillRoom(roomId: string, adminId: string): { ok: true } | { ok: false; error: string } {
|
fillRoom(roomId: string, adminId: string): FillResult {
|
||||||
const room = this.rooms.get(roomId);
|
const room = this.rooms.get(roomId);
|
||||||
if (!room) return { ok: false, error: "Room not found" };
|
if (!room) return { ok: false, error: "Room not found" };
|
||||||
if (room.status !== "waiting") return { ok: false, error: "Game is not in waiting state" };
|
if (room.status !== "waiting") return { ok: false, error: "Game is not in waiting state" };
|
||||||
@@ -185,9 +214,27 @@ export class RoomManager {
|
|||||||
room.players.push(adminId);
|
room.players.push(adminId);
|
||||||
}
|
}
|
||||||
|
|
||||||
room.state = plugin.createInitialState(room.players);
|
// Defer start when bets are involved
|
||||||
|
if (room.betAmount > 0) {
|
||||||
|
this.refreshWaitingCleanup(roomId, room);
|
||||||
|
this.emitter.emit("room:list:changed");
|
||||||
|
return { ok: true, readyToStart: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startGame(roomId);
|
||||||
|
return { ok: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initialize game state and transition room to playing. */
|
||||||
|
startGame(roomId: string): { ok: true } | { ok: false; error: string } {
|
||||||
|
const room = this.rooms.get(roomId);
|
||||||
|
if (!room) return { ok: false, error: "Room not found" };
|
||||||
|
if (room.status !== "waiting") return { ok: false, error: "Game is not in waiting state" };
|
||||||
|
|
||||||
|
const plugin = gameRegistry.get(room.gameSlug)!;
|
||||||
|
room.state = plugin.createInitialState(room.players, room.options);
|
||||||
room.status = "playing";
|
room.status = "playing";
|
||||||
this.scheduleCleanup(roomId, ROOM_CONFIG.PLAYING_MAX_MS);
|
this.scheduleCleanup(roomId, this.config.PLAYING_MAX_MS);
|
||||||
|
|
||||||
const spectatorView = plugin.getSpectatorView(room.state);
|
const spectatorView = plugin.getSpectatorView(room.state);
|
||||||
const playerViews = new Map<string, unknown>();
|
const playerViews = new Map<string, unknown>();
|
||||||
@@ -199,6 +246,20 @@ export class RoomManager {
|
|||||||
return { ok: true };
|
return { ok: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Remove a player from a waiting room (used when bet deduction fails). */
|
||||||
|
removePlayer(roomId: string, playerId: string): void {
|
||||||
|
const room = this.rooms.get(roomId);
|
||||||
|
if (!room || room.status !== "waiting") return;
|
||||||
|
const idx = room.players.indexOf(playerId);
|
||||||
|
if (idx !== -1) room.players.splice(idx, 1);
|
||||||
|
if (room.players.length === 0) {
|
||||||
|
this.deleteRoom(roomId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.refreshWaitingCleanup(roomId, room);
|
||||||
|
this.emitter.emit("room:list:changed");
|
||||||
|
}
|
||||||
|
|
||||||
getRoom(roomId: string): Room | undefined {
|
getRoom(roomId: string): Room | undefined {
|
||||||
return this.rooms.get(roomId);
|
return this.rooms.get(roomId);
|
||||||
}
|
}
|
||||||
@@ -217,6 +278,7 @@ export class RoomManager {
|
|||||||
maxPlayers: plugin?.maxPlayers ?? 0,
|
maxPlayers: plugin?.maxPlayers ?? 0,
|
||||||
spectatorCount: room.spectators.size,
|
spectatorCount: room.spectators.size,
|
||||||
status: room.status,
|
status: room.status,
|
||||||
|
betAmount: room.betAmount,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return summaries;
|
return summaries;
|
||||||
@@ -242,6 +304,11 @@ export class RoomManager {
|
|||||||
this.cleanupTimers.set(roomId, timer);
|
this.cleanupTimers.set(roomId, timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private refreshWaitingCleanup(roomId: string, room: Room): void {
|
||||||
|
if (room.status !== "waiting") return;
|
||||||
|
this.scheduleCleanup(roomId, this.config.WAITING_CLEANUP_MS);
|
||||||
|
}
|
||||||
|
|
||||||
private clearCleanup(roomId: string): void {
|
private clearCleanup(roomId: string): void {
|
||||||
const existing = this.cleanupTimers.get(roomId);
|
const existing = this.cleanupTimers.get(roomId);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { RoundSettlement } from "@shared/games/types";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export interface Room {
|
export interface Room {
|
||||||
@@ -9,6 +10,10 @@ export interface Room {
|
|||||||
state: unknown;
|
state: unknown;
|
||||||
status: "waiting" | "playing" | "finished";
|
status: "waiting" | "playing" | "finished";
|
||||||
createdAt: number;
|
createdAt: number;
|
||||||
|
options?: Record<string, unknown>;
|
||||||
|
betAmount: number;
|
||||||
|
/** Guard against double bet-deduction when two joins race */
|
||||||
|
betsPending?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RoomSummary {
|
export interface RoomSummary {
|
||||||
@@ -20,6 +25,7 @@ export interface RoomSummary {
|
|||||||
maxPlayers: number;
|
maxPlayers: number;
|
||||||
spectatorCount: number;
|
spectatorCount: number;
|
||||||
status: "waiting" | "playing" | "finished";
|
status: "waiting" | "playing" | "finished";
|
||||||
|
betAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlayerInfo {
|
export interface PlayerInfo {
|
||||||
@@ -28,7 +34,7 @@ export interface PlayerInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const GameWsClientSchema = z.discriminatedUnion("type", [
|
export const GameWsClientSchema = z.discriminatedUnion("type", [
|
||||||
z.object({ type: z.literal("CREATE_ROOM"), gameType: z.string() }),
|
z.object({ type: z.literal("CREATE_ROOM"), gameType: z.string(), options: z.looseObject({}).optional() }),
|
||||||
z.object({
|
z.object({
|
||||||
type: z.literal("JOIN_ROOM"),
|
type: z.literal("JOIN_ROOM"),
|
||||||
roomId: z.string(),
|
roomId: z.string(),
|
||||||
@@ -39,6 +45,7 @@ export const GameWsClientSchema = z.discriminatedUnion("type", [
|
|||||||
// Use looseObject for GAME_ACTION to avoid Zod bug with record()
|
// Use looseObject for GAME_ACTION to avoid Zod bug with record()
|
||||||
z.object({ type: z.literal("GAME_ACTION"), roomId: z.string(), action: z.looseObject({}, { message: "Invalid action" }) }),
|
z.object({ type: z.literal("GAME_ACTION"), roomId: z.string(), action: z.looseObject({}, { message: "Invalid action" }) }),
|
||||||
z.object({ type: z.literal("FILL_ROOM"), roomId: z.string() }),
|
z.object({ type: z.literal("FILL_ROOM"), roomId: z.string() }),
|
||||||
|
z.object({ type: z.literal("START_GAME"), roomId: z.string() }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export type GameWsClientMessage = z.infer<typeof GameWsClientSchema>;
|
export type GameWsClientMessage = z.infer<typeof GameWsClientSchema>;
|
||||||
@@ -50,8 +57,9 @@ export type GameWsServerMessage =
|
|||||||
| { type: "PLAYER_JOINED"; roomId: string; player: PlayerInfo; joinedAs: "player" | "spectator" }
|
| { type: "PLAYER_JOINED"; roomId: string; player: PlayerInfo; joinedAs: "player" | "spectator" }
|
||||||
| { type: "PLAYER_LEFT"; roomId: string; playerId: string }
|
| { type: "PLAYER_LEFT"; roomId: string; playerId: string }
|
||||||
| { type: "GAME_STARTED"; roomId: string; state: unknown }
|
| { type: "GAME_STARTED"; roomId: string; state: unknown }
|
||||||
| { type: "GAME_ENDED"; roomId: string; winner: string | null; reason: string }
|
| { type: "GAME_ENDED"; roomId: string; winner: string | null; reason: string; payout?: { amount: number; refunded?: boolean } }
|
||||||
| { type: "ROOM_CREATED"; roomId: string; gameSlug: string }
|
| { type: "ROOM_CREATED"; roomId: string; gameSlug: string }
|
||||||
| { type: "JOIN_RESULT"; roomId: string; joinedAs: "player" | "spectator"; roomStatus: "waiting" | "playing" | "finished"; players: PlayerInfo[]; spectators: PlayerInfo[]; state?: unknown }
|
| { type: "JOIN_RESULT"; roomId: string; joinedAs: "player" | "spectator"; roomStatus: "waiting" | "playing" | "finished"; players: PlayerInfo[]; spectators: PlayerInfo[]; state?: unknown; roomOptions?: { betAmount?: number; timeControl?: string } }
|
||||||
|
| { type: "ROUND_SETTLED"; roomId: string; settlements: Record<string, RoundSettlement> }
|
||||||
| { type: "SESSION_REPLACED"; roomId: string }
|
| { type: "SESSION_REPLACED"; roomId: string }
|
||||||
| { type: "ERROR"; message: string };
|
| { type: "ERROR"; message: string };
|
||||||
|
|||||||
103
api/src/routes/auth.routes.test.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from "bun:test";
|
||||||
|
|
||||||
|
const findFirst = mock(async () => ({ id: 123n }));
|
||||||
|
|
||||||
|
mock.module("@shared/db/DrizzleClient", () => ({
|
||||||
|
DrizzleClient: {
|
||||||
|
query: {
|
||||||
|
users: {
|
||||||
|
findFirst,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module("@shared/lib/logger", () => ({
|
||||||
|
logger: {
|
||||||
|
error: () => { },
|
||||||
|
info: () => { },
|
||||||
|
warn: () => { },
|
||||||
|
debug: () => { },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { authRoutes, getSession } from "./auth.routes";
|
||||||
|
|
||||||
|
describe("Auth Routes", () => {
|
||||||
|
let fetchSpy: ReturnType<typeof spyOn> | null = null;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
process.env.DISCORD_CLIENT_ID = "client-id";
|
||||||
|
process.env.DISCORD_CLIENT_SECRET = "client-secret";
|
||||||
|
process.env.SESSION_SECRET = "session-secret";
|
||||||
|
process.env.PANEL_BASE_URL = "http://localhost:3000";
|
||||||
|
process.env.ADMIN_USER_IDS = "123";
|
||||||
|
findFirst.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fetchSpy?.mockRestore();
|
||||||
|
fetchSpy = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates a signed session cookie during OAuth callback", async () => {
|
||||||
|
const loginUrl = new URL("http://localhost/auth/discord?return_to=http://localhost:5173/admin");
|
||||||
|
const loginRes = await authRoutes.handler({
|
||||||
|
req: new Request(loginUrl, { method: "GET" }),
|
||||||
|
url: loginUrl,
|
||||||
|
method: "GET",
|
||||||
|
pathname: "/auth/discord",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(loginRes?.status).toBe(302);
|
||||||
|
|
||||||
|
const redirectLocation = loginRes?.headers.get("Location");
|
||||||
|
expect(redirectLocation).not.toBeNull();
|
||||||
|
|
||||||
|
const state = new URL(redirectLocation!).searchParams.get("state");
|
||||||
|
expect(state).not.toBeNull();
|
||||||
|
|
||||||
|
fetchSpy = spyOn(globalThis, "fetch");
|
||||||
|
fetchSpy.mockResolvedValueOnce(new Response(JSON.stringify({ access_token: "discord-token" }), { status: 200 }));
|
||||||
|
fetchSpy.mockResolvedValueOnce(new Response(JSON.stringify({
|
||||||
|
id: "123",
|
||||||
|
username: "aurora-admin",
|
||||||
|
avatar: null,
|
||||||
|
}), { status: 200 }));
|
||||||
|
|
||||||
|
const callbackUrl = new URL(`http://localhost/auth/callback?code=oauth-code&state=${encodeURIComponent(state!)}`);
|
||||||
|
const callbackRes = await authRoutes.handler({
|
||||||
|
req: new Request(callbackUrl, { method: "GET" }),
|
||||||
|
url: callbackUrl,
|
||||||
|
method: "GET",
|
||||||
|
pathname: "/auth/callback",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callbackRes?.status).toBe(302);
|
||||||
|
expect(callbackRes?.headers.get("Location")).toBe("/admin");
|
||||||
|
|
||||||
|
const setCookie = callbackRes?.headers.get("Set-Cookie");
|
||||||
|
expect(setCookie).toContain("aurora_session=");
|
||||||
|
|
||||||
|
const sessionCookie = setCookie!.split(";")[0]!;
|
||||||
|
const session = getSession(new Request("http://localhost/api/me", {
|
||||||
|
headers: { cookie: sessionCookie },
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(session).toEqual({
|
||||||
|
discordId: "123",
|
||||||
|
username: "aurora-admin",
|
||||||
|
avatar: null,
|
||||||
|
role: "admin",
|
||||||
|
expiresAt: expect.any(Number),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects tampered session cookies", () => {
|
||||||
|
const session = getSession(new Request("http://localhost/api/me", {
|
||||||
|
headers: { cookie: "aurora_session=not-a-valid-token" },
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(session).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,6 +3,8 @@
|
|||||||
* Handles login flow, callback, logout, and session management.
|
* Handles login flow, callback, logout, and session management.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Buffer } from "node:buffer";
|
||||||
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
||||||
import type { RouteContext, RouteModule } from "./types";
|
import type { RouteContext, RouteModule } from "./types";
|
||||||
import { jsonResponse, errorResponse } from "./utils";
|
import { jsonResponse, errorResponse } from "./utils";
|
||||||
import { logger } from "@shared/lib/logger";
|
import { logger } from "@shared/lib/logger";
|
||||||
@@ -10,7 +12,7 @@ import { DrizzleClient } from "@shared/db/DrizzleClient";
|
|||||||
import { users } from "@shared/db/schema";
|
import { users } from "@shared/db/schema";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
// In-memory session store: token → { discordId, username, avatar, role, expiresAt }
|
// Signed session payload stored in the aurora_session cookie.
|
||||||
export interface Session {
|
export interface Session {
|
||||||
discordId: string;
|
discordId: string;
|
||||||
username: string;
|
username: string;
|
||||||
@@ -19,10 +21,21 @@ export interface Session {
|
|||||||
expiresAt: number;
|
expiresAt: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessions = new Map<string, Session>();
|
interface SessionTokenPayload extends Session {
|
||||||
const redirects = new Map<string, string>(); // redirect token -> return_to URL
|
v: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OAuthStatePayload {
|
||||||
|
exp: number;
|
||||||
|
returnTo: string;
|
||||||
|
v: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const COOKIE_NAME = "aurora_session";
|
||||||
const SESSION_MAX_AGE = 7 * 24 * 60 * 60 * 1000; // 7 days
|
const SESSION_MAX_AGE = 7 * 24 * 60 * 60 * 1000; // 7 days
|
||||||
|
const OAUTH_STATE_MAX_AGE = 10 * 60 * 1000; // 10 minutes
|
||||||
|
const TOKEN_NAMESPACE = "aurora.auth";
|
||||||
|
const TOKEN_VERSION = "v1";
|
||||||
|
|
||||||
function getEnv(key: string): string {
|
function getEnv(key: string): string {
|
||||||
const val = process.env[key];
|
const val = process.env[key];
|
||||||
@@ -30,15 +43,70 @@ function getEnv(key: string): string {
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSessionSecret(required: boolean = false): string | null {
|
||||||
|
const secret = process.env.SESSION_SECRET ?? process.env.DISCORD_CLIENT_SECRET ?? null;
|
||||||
|
if (!secret && required) {
|
||||||
|
throw new Error("Missing env: SESSION_SECRET or DISCORD_CLIENT_SECRET");
|
||||||
|
}
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireSessionSecret(): string {
|
||||||
|
return getSessionSecret(true)!;
|
||||||
|
}
|
||||||
|
|
||||||
function getAdminIds(): string[] {
|
function getAdminIds(): string[] {
|
||||||
const raw = process.env.ADMIN_USER_IDS ?? "";
|
const raw = process.env.ADMIN_USER_IDS ?? "";
|
||||||
return raw.split(",").map(s => s.trim()).filter(Boolean);
|
return raw.split(",").map(s => s.trim()).filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateToken(): string {
|
function encodeBase64Url(value: string): string {
|
||||||
const bytes = new Uint8Array(32);
|
return Buffer.from(value, "utf8").toString("base64url");
|
||||||
crypto.getRandomValues(bytes);
|
}
|
||||||
return Array.from(bytes, b => b.toString(16).padStart(2, "0")).join("");
|
|
||||||
|
function decodeBase64Url(value: string): string {
|
||||||
|
return Buffer.from(value, "base64url").toString("utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
function signValue(kind: string, encodedPayload: string, secret: string): string {
|
||||||
|
return createHmac("sha256", secret)
|
||||||
|
.update(`${TOKEN_NAMESPACE}.${kind}.${encodedPayload}`)
|
||||||
|
.digest("base64url");
|
||||||
|
}
|
||||||
|
|
||||||
|
function serializeSignedToken(kind: string, payload: SessionTokenPayload | OAuthStatePayload, secret: string): string {
|
||||||
|
const encodedPayload = encodeBase64Url(JSON.stringify(payload));
|
||||||
|
const signature = signValue(kind, encodedPayload, secret);
|
||||||
|
return `${TOKEN_VERSION}.${encodedPayload}.${signature}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSignedToken<T>(token: string | undefined, kind: string): T | null {
|
||||||
|
if (!token) return null;
|
||||||
|
|
||||||
|
const secret = getSessionSecret();
|
||||||
|
if (!secret) return null;
|
||||||
|
|
||||||
|
const parts = token.split(".");
|
||||||
|
if (parts.length !== 3) return null;
|
||||||
|
|
||||||
|
const version = parts[0];
|
||||||
|
const encodedPayload = parts[1];
|
||||||
|
const providedSignature = parts[2];
|
||||||
|
if (version !== TOKEN_VERSION) return null;
|
||||||
|
if (!encodedPayload || !providedSignature) return null;
|
||||||
|
|
||||||
|
const expectedSignature = signValue(kind, encodedPayload, secret);
|
||||||
|
const providedBuffer = Buffer.from(providedSignature);
|
||||||
|
const expectedBuffer = Buffer.from(expectedSignature);
|
||||||
|
|
||||||
|
if (providedBuffer.length !== expectedBuffer.length) return null;
|
||||||
|
if (!timingSafeEqual(providedBuffer, expectedBuffer)) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(decodeBase64Url(encodedPayload)) as T;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBaseUrl(): string {
|
function getBaseUrl(): string {
|
||||||
@@ -55,18 +123,65 @@ function parseCookies(header: string | null): Record<string, string> {
|
|||||||
return cookies;
|
return cookies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sanitizeReturnTo(rawReturnTo: string | null, baseUrl: string): string {
|
||||||
|
if (!rawReturnTo || rawReturnTo.length > 1024) return "/";
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (rawReturnTo.startsWith("/") && !rawReturnTo.startsWith("//")) {
|
||||||
|
return rawReturnTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = new URL(rawReturnTo, baseUrl);
|
||||||
|
const allowedBase = new URL(baseUrl);
|
||||||
|
const isLocalhostRedirect = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
||||||
|
|
||||||
|
if (parsed.origin === allowedBase.origin || isLocalhostRedirect) {
|
||||||
|
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCookieAttributes(maxAgeSeconds?: number): string {
|
||||||
|
const attrs = [
|
||||||
|
"Path=/",
|
||||||
|
"HttpOnly",
|
||||||
|
"SameSite=Lax",
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (new URL(getBaseUrl()).protocol === "https:") {
|
||||||
|
attrs.push("Secure");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore invalid PANEL_BASE_URL here; handlers that need it will fail explicitly.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof maxAgeSeconds === "number") {
|
||||||
|
attrs.push(`Max-Age=${maxAgeSeconds}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs.join("; ");
|
||||||
|
}
|
||||||
|
|
||||||
/** Get session from request cookie */
|
/** Get session from request cookie */
|
||||||
export function getSession(req: Request): Session | null {
|
export function getSession(req: Request): Session | null {
|
||||||
const cookies = parseCookies(req.headers.get("cookie"));
|
const cookies = parseCookies(req.headers.get("cookie"));
|
||||||
const token = cookies["aurora_session"];
|
const payload = parseSignedToken<SessionTokenPayload>(cookies[COOKIE_NAME], "session");
|
||||||
if (!token) return null;
|
|
||||||
const session = sessions.get(token);
|
if (!payload || payload.v !== 1) return null;
|
||||||
if (!session) return null;
|
if (Date.now() > payload.expiresAt) return null;
|
||||||
if (Date.now() > session.expiresAt) {
|
|
||||||
sessions.delete(token);
|
return {
|
||||||
return null;
|
discordId: payload.discordId,
|
||||||
}
|
username: payload.username,
|
||||||
return session;
|
avatar: payload.avatar,
|
||||||
|
role: payload.role,
|
||||||
|
expiresAt: payload.expiresAt,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Check if request is authenticated as admin */
|
/** Check if request is authenticated as admin */
|
||||||
@@ -84,20 +199,22 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
const baseUrl = getBaseUrl();
|
const baseUrl = getBaseUrl();
|
||||||
const redirectUri = encodeURIComponent(`${baseUrl}/auth/callback`);
|
const redirectUri = encodeURIComponent(`${baseUrl}/auth/callback`);
|
||||||
const scope = "identify+email";
|
const scope = "identify+email";
|
||||||
|
const secret = requireSessionSecret();
|
||||||
|
|
||||||
// Store return_to URL if provided
|
// Store return_to URL in signed OAuth state
|
||||||
const returnTo = ctx.url.searchParams.get("return_to") || "/";
|
const returnTo = sanitizeReturnTo(ctx.url.searchParams.get("return_to"), baseUrl);
|
||||||
const redirectToken = generateToken();
|
const state = serializeSignedToken("oauth", {
|
||||||
redirects.set(redirectToken, returnTo);
|
exp: Date.now() + OAUTH_STATE_MAX_AGE,
|
||||||
|
returnTo,
|
||||||
|
v: 1,
|
||||||
|
}, secret);
|
||||||
|
|
||||||
const url = `https://discord.com/oauth2/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}`;
|
const url = `https://discord.com/oauth2/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${encodeURIComponent(state)}`;
|
||||||
|
|
||||||
// Set a temporary cookie with the redirect token
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 302,
|
status: 302,
|
||||||
headers: {
|
headers: {
|
||||||
Location: url,
|
Location: url,
|
||||||
"Set-Cookie": `aurora_redirect=${redirectToken}; Path=/; Max-Age=600; SameSite=Lax`,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -116,8 +233,13 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
const clientSecret = getEnv("DISCORD_CLIENT_SECRET");
|
const clientSecret = getEnv("DISCORD_CLIENT_SECRET");
|
||||||
const baseUrl = getBaseUrl();
|
const baseUrl = getBaseUrl();
|
||||||
const redirectUri = `${baseUrl}/auth/callback`;
|
const redirectUri = `${baseUrl}/auth/callback`;
|
||||||
|
const secret = requireSessionSecret();
|
||||||
|
const statePayload = parseSignedToken<OAuthStatePayload>(ctx.url.searchParams.get("state") ?? undefined, "oauth");
|
||||||
|
|
||||||
|
if (!statePayload || statePayload.v !== 1 || Date.now() > statePayload.exp) {
|
||||||
|
return errorResponse("Invalid OAuth state", 400);
|
||||||
|
}
|
||||||
|
|
||||||
// Exchange code for token
|
|
||||||
const tokenRes = await fetch("https://discord.com/api/oauth2/token", {
|
const tokenRes = await fetch("https://discord.com/api/oauth2/token", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
@@ -165,40 +287,24 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
const adminIds = getAdminIds();
|
const adminIds = getAdminIds();
|
||||||
const role: "admin" | "player" = adminIds.includes(user.id) ? "admin" : "player";
|
const role: "admin" | "player" = adminIds.includes(user.id) ? "admin" : "player";
|
||||||
|
|
||||||
// Create session
|
// Create signed session cookie
|
||||||
const token = generateToken();
|
const sessionToken = serializeSignedToken("session", {
|
||||||
sessions.set(token, {
|
|
||||||
discordId: user.id,
|
discordId: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
avatar: user.avatar,
|
avatar: user.avatar,
|
||||||
role,
|
role,
|
||||||
expiresAt: Date.now() + SESSION_MAX_AGE,
|
expiresAt: Date.now() + SESSION_MAX_AGE,
|
||||||
});
|
v: 1,
|
||||||
|
}, secret);
|
||||||
|
|
||||||
logger.info("auth", `Login: ${user.username} (${user.id}) as ${role}`);
|
logger.info("auth", `Login: ${user.username} (${user.id}) as ${role}`);
|
||||||
|
|
||||||
// Get return_to URL from redirect token cookie
|
|
||||||
const cookies = parseCookies(ctx.req.headers.get("cookie"));
|
|
||||||
const redirectToken = cookies["aurora_redirect"];
|
|
||||||
let returnTo = redirectToken && redirects.get(redirectToken) ? redirects.get(redirectToken)! : "/";
|
|
||||||
if (redirectToken) redirects.delete(redirectToken);
|
|
||||||
|
|
||||||
// Only allow redirects to localhost or relative paths (prevent open redirect)
|
|
||||||
try {
|
|
||||||
const parsed = new URL(returnTo, baseUrl);
|
|
||||||
if (parsed.hostname !== "localhost" && parsed.hostname !== "127.0.0.1") {
|
|
||||||
returnTo = "/";
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
returnTo = "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect to panel with session cookie
|
// Redirect to panel with session cookie
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 302,
|
status: 302,
|
||||||
headers: {
|
headers: {
|
||||||
Location: returnTo,
|
Location: sanitizeReturnTo(statePayload.returnTo, baseUrl),
|
||||||
"Set-Cookie": `aurora_session=${token}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${SESSION_MAX_AGE / 1000}`,
|
"Set-Cookie": `${COOKIE_NAME}=${sessionToken}; ${buildCookieAttributes(SESSION_MAX_AGE / 1000)}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -209,14 +315,10 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
|
|
||||||
// POST /auth/logout — clear session
|
// POST /auth/logout — clear session
|
||||||
if (pathname === "/auth/logout" && method === "POST") {
|
if (pathname === "/auth/logout" && method === "POST") {
|
||||||
const cookies = parseCookies(ctx.req.headers.get("cookie"));
|
|
||||||
const token = cookies["aurora_session"];
|
|
||||||
if (token) sessions.delete(token);
|
|
||||||
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
"Set-Cookie": "aurora_session=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0",
|
"Set-Cookie": `${COOKIE_NAME}=; ${buildCookieAttributes(0)}`,
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
114
api/src/routes/index.authz.test.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { beforeEach, describe, expect, it, mock } from "bun:test";
|
||||||
|
|
||||||
|
let currentSession: { discordId: string; username: string; role: "admin" | "player"; expiresAt: number } | null = null;
|
||||||
|
|
||||||
|
mock.module("./auth.routes", () => ({
|
||||||
|
authRoutes: { name: "auth", handler: () => null },
|
||||||
|
getSession: () => currentSession,
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module("./health.routes", () => ({
|
||||||
|
healthRoutes: {
|
||||||
|
name: "health",
|
||||||
|
handler: ({ pathname }: { pathname: string }) =>
|
||||||
|
pathname === "/api/health"
|
||||||
|
? Response.json({ status: "ok" }, { status: 200 })
|
||||||
|
: null,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module("./stats.routes", () => ({
|
||||||
|
statsRoutes: {
|
||||||
|
name: "stats",
|
||||||
|
handler: ({ pathname }: { pathname: string }) =>
|
||||||
|
pathname === "/api/stats"
|
||||||
|
? Response.json({ ok: true }, { status: 200 })
|
||||||
|
: null,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module("./actions.routes", () => ({ actionsRoutes: { name: "actions", handler: () => null } }));
|
||||||
|
mock.module("./quests.routes", () => ({ questsRoutes: { name: "quests", handler: () => null } }));
|
||||||
|
mock.module("./settings.routes", () => ({ settingsRoutes: { name: "settings", handler: () => null } }));
|
||||||
|
mock.module("./guild-settings.routes", () => ({ guildSettingsRoutes: { name: "guild-settings", handler: () => null } }));
|
||||||
|
mock.module("./items.routes", () => ({ itemsRoutes: { name: "items", handler: () => null } }));
|
||||||
|
mock.module("./classes.routes", () => ({ classesRoutes: { name: "classes", handler: () => null } }));
|
||||||
|
mock.module("./moderation.routes", () => ({ moderationRoutes: { name: "moderation", handler: () => null } }));
|
||||||
|
mock.module("./transactions.routes", () => ({ transactionsRoutes: { name: "transactions", handler: () => null } }));
|
||||||
|
mock.module("./lootdrops.routes", () => ({ lootdropsRoutes: { name: "lootdrops", handler: () => null } }));
|
||||||
|
mock.module("./assets.routes", () => ({ assetsRoutes: { name: "assets", handler: () => null } }));
|
||||||
|
mock.module("@shared/modules/user/user.service", () => ({
|
||||||
|
userService: {
|
||||||
|
getUserById: async (id: string) => ({ id, username: `user-${id}` }),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
mock.module("@shared/modules/inventory/inventory.service", () => ({
|
||||||
|
inventoryService: {
|
||||||
|
getInventory: async (id: string) => [{ userId: id, itemId: 1, quantity: 1n }],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
mock.module("@shared/lib/logger", () => ({
|
||||||
|
logger: {
|
||||||
|
error: () => { },
|
||||||
|
info: () => { },
|
||||||
|
warn: () => { },
|
||||||
|
debug: () => { },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { handleRequest } from "./index";
|
||||||
|
|
||||||
|
describe("Route Authorization", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
currentSession = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects unauthenticated protected API requests", async () => {
|
||||||
|
const url = new URL("http://localhost/api/users/123");
|
||||||
|
const res = await handleRequest(new Request(url, { method: "GET" }), url);
|
||||||
|
|
||||||
|
expect(res?.status).toBe(401);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("blocks players from admin user routes", async () => {
|
||||||
|
currentSession = {
|
||||||
|
discordId: "123",
|
||||||
|
username: "player",
|
||||||
|
role: "player",
|
||||||
|
expiresAt: Date.now() + 60_000,
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = new URL("http://localhost/api/users/456");
|
||||||
|
const res = await handleRequest(new Request(url, { method: "GET" }), url);
|
||||||
|
|
||||||
|
expect(res?.status).toBe(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows players to access self-service API routes", async () => {
|
||||||
|
currentSession = {
|
||||||
|
discordId: "123",
|
||||||
|
username: "player",
|
||||||
|
role: "player",
|
||||||
|
expiresAt: Date.now() + 60_000,
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = new URL("http://localhost/api/me/inventory");
|
||||||
|
const res = await handleRequest(new Request(url, { method: "GET" }), url);
|
||||||
|
|
||||||
|
expect(res?.status).toBe(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows admins to access admin user routes", async () => {
|
||||||
|
currentSession = {
|
||||||
|
discordId: "1",
|
||||||
|
username: "admin",
|
||||||
|
role: "admin",
|
||||||
|
expiresAt: Date.now() + 60_000,
|
||||||
|
};
|
||||||
|
|
||||||
|
const url = new URL("http://localhost/api/users/456");
|
||||||
|
const res = await handleRequest(new Request(url, { method: "GET" }), url);
|
||||||
|
|
||||||
|
expect(res?.status).toBe(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { RouteContext, RouteModule } from "./types";
|
import type { RouteContext, RouteModule } from "./types";
|
||||||
import { authRoutes, isAuthenticated, getSession } from "./auth.routes";
|
import { authRoutes, getSession } from "./auth.routes";
|
||||||
import { healthRoutes } from "./health.routes";
|
import { healthRoutes } from "./health.routes";
|
||||||
import { statsRoutes } from "./stats.routes";
|
import { statsRoutes } from "./stats.routes";
|
||||||
import { actionsRoutes } from "./actions.routes";
|
import { actionsRoutes } from "./actions.routes";
|
||||||
@@ -75,14 +75,11 @@ export async function handleRequest(req: Request, url: URL): Promise<Response |
|
|||||||
return errorResponse("Unauthorized", 401);
|
return errorResponse("Unauthorized", 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin-only routes: everything except stats and own user data
|
// Player routes are explicitly allow-listed. Everything else is admin-only.
|
||||||
const playerAllowedPrefixes = ["/api/stats", "/api/health"];
|
const playerAllowedPrefixes = ["/api/stats", "/api/health", "/api/me"];
|
||||||
const isPlayerAllowed = playerAllowedPrefixes.some(p => ctx.pathname.startsWith(p));
|
const isPlayerAllowed = playerAllowedPrefixes.some(p => ctx.pathname.startsWith(p));
|
||||||
|
|
||||||
// Players can access their own user data
|
if (session.role === "player" && !isPlayerAllowed) {
|
||||||
const isOwnUserRoute = ctx.pathname.match(/^\/api\/users\/\d+/) && session.role === "player";
|
|
||||||
|
|
||||||
if (session.role === "player" && !isPlayerAllowed && !isOwnUserRoute) {
|
|
||||||
return errorResponse("Admin access required", 403);
|
return errorResponse("Admin access required", 403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export const UpdateUserSchema = z.object({
|
|||||||
dailyStreak: z.coerce.number().int().min(0).optional(),
|
dailyStreak: z.coerce.number().int().min(0).optional(),
|
||||||
isActive: z.boolean().optional(),
|
isActive: z.boolean().optional(),
|
||||||
settings: z.record(z.string(), z.any()).optional(),
|
settings: z.record(z.string(), z.any()).optional(),
|
||||||
classId: z.union([z.string(), z.number()]).optional(),
|
classId: z.union([z.string(), z.number()]).nullable().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
140
api/src/routes/users.routes.test.ts
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import { beforeEach, describe, expect, it, mock } from "bun:test";
|
||||||
|
|
||||||
|
let currentSession: { discordId: string; username: string; role: "admin" | "player"; expiresAt: number } | null = null;
|
||||||
|
|
||||||
|
const getUserById = mock(async (id: string) => ({
|
||||||
|
id,
|
||||||
|
username: id === "123" ? "player-one" : "user",
|
||||||
|
level: 5,
|
||||||
|
xp: 100n,
|
||||||
|
balance: 250n,
|
||||||
|
className: null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const updateUser = mock(async (id: string, data: Record<string, unknown>) => ({
|
||||||
|
id,
|
||||||
|
...data,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const getInventory = mock(async (id: string) => [{ userId: id, itemId: 1, quantity: 2n }]);
|
||||||
|
const addItem = mock(async (userId: string, itemId: number, quantity: bigint) => ({ userId, itemId, quantity }));
|
||||||
|
const removeItem = mock(async () => undefined);
|
||||||
|
|
||||||
|
mock.module("./auth.routes", () => ({
|
||||||
|
getSession: () => currentSession,
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module("@shared/modules/user/user.service", () => ({
|
||||||
|
userService: {
|
||||||
|
getUserById,
|
||||||
|
updateUser,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module("@shared/modules/inventory/inventory.service", () => ({
|
||||||
|
inventoryService: {
|
||||||
|
getInventory,
|
||||||
|
addItem,
|
||||||
|
removeItem,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module("@shared/lib/logger", () => ({
|
||||||
|
logger: {
|
||||||
|
error: () => { },
|
||||||
|
info: () => { },
|
||||||
|
warn: () => { },
|
||||||
|
debug: () => { },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { usersRoutes } from "./users.routes";
|
||||||
|
|
||||||
|
describe("Users Routes", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
currentSession = {
|
||||||
|
discordId: "123",
|
||||||
|
username: "player",
|
||||||
|
role: "player",
|
||||||
|
expiresAt: Date.now() + 60_000,
|
||||||
|
};
|
||||||
|
getUserById.mockClear();
|
||||||
|
updateUser.mockClear();
|
||||||
|
getInventory.mockClear();
|
||||||
|
addItem.mockClear();
|
||||||
|
removeItem.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("serves the authenticated user through /api/me", async () => {
|
||||||
|
const url = new URL("http://localhost/api/me");
|
||||||
|
const res = await usersRoutes.handler({
|
||||||
|
req: new Request(url, { method: "GET" }),
|
||||||
|
url,
|
||||||
|
method: "GET",
|
||||||
|
pathname: "/api/me",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res?.status).toBe(200);
|
||||||
|
expect(getUserById).toHaveBeenCalledWith("123");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("serves the authenticated user's inventory through /api/me/inventory", async () => {
|
||||||
|
const url = new URL("http://localhost/api/me/inventory");
|
||||||
|
const res = await usersRoutes.handler({
|
||||||
|
req: new Request(url, { method: "GET" }),
|
||||||
|
url,
|
||||||
|
method: "GET",
|
||||||
|
pathname: "/api/me/inventory",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res?.status).toBe(200);
|
||||||
|
expect(getInventory).toHaveBeenCalledWith("123");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validates user updates before calling the service", async () => {
|
||||||
|
const url = new URL("http://localhost/api/users/123");
|
||||||
|
const res = await usersRoutes.handler({
|
||||||
|
req: new Request(url, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ level: -1 }),
|
||||||
|
}),
|
||||||
|
url,
|
||||||
|
method: "PUT",
|
||||||
|
pathname: "/api/users/123",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res?.status).toBe(400);
|
||||||
|
expect(updateUser).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validates inventory additions before calling the service", async () => {
|
||||||
|
const url = new URL("http://localhost/api/users/123/inventory");
|
||||||
|
const res = await usersRoutes.handler({
|
||||||
|
req: new Request(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ itemId: 1, quantity: 0 }),
|
||||||
|
}),
|
||||||
|
url,
|
||||||
|
method: "POST",
|
||||||
|
pathname: "/api/users/123/inventory",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res?.status).toBe(400);
|
||||||
|
expect(addItem).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("validates inventory removal query params before calling the service", async () => {
|
||||||
|
const url = new URL("http://localhost/api/users/123/inventory/1?amount=0");
|
||||||
|
const res = await usersRoutes.handler({
|
||||||
|
req: new Request(url, { method: "DELETE" }),
|
||||||
|
url,
|
||||||
|
method: "DELETE",
|
||||||
|
pathname: "/api/users/123/inventory/1",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res?.status).toBe(400);
|
||||||
|
expect(removeItem).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -8,16 +8,19 @@ import {
|
|||||||
jsonResponse,
|
jsonResponse,
|
||||||
errorResponse,
|
errorResponse,
|
||||||
parseBody,
|
parseBody,
|
||||||
parseIdFromPath,
|
parseQuery,
|
||||||
parseStringIdFromPath,
|
parseStringIdFromPath,
|
||||||
withErrorHandling
|
withErrorHandling
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import { UpdateUserSchema, InventoryAddSchema } from "./schemas";
|
import { InventoryAddSchema, InventoryRemoveQuerySchema, UpdateUserSchema, UserQuerySchema } from "./schemas";
|
||||||
|
import { getSession } from "./auth.routes";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users routes handler.
|
* Users routes handler.
|
||||||
*
|
*
|
||||||
* Endpoints:
|
* Endpoints:
|
||||||
|
* - GET /api/me - Get current authenticated user
|
||||||
|
* - GET /api/me/inventory - Get current authenticated user's inventory
|
||||||
* - GET /api/users - List users with filters
|
* - GET /api/users - List users with filters
|
||||||
* - GET /api/users/:id - Get single user
|
* - GET /api/users/:id - Get single user
|
||||||
* - PUT /api/users/:id - Update user
|
* - PUT /api/users/:id - Update user
|
||||||
@@ -30,6 +33,37 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
|
|
||||||
// Only handle requests to /api/users*
|
// Only handle requests to /api/users*
|
||||||
if (!pathname.startsWith("/api/users")) {
|
if (!pathname.startsWith("/api/users")) {
|
||||||
|
if (pathname === "/api/me" && method === "GET") {
|
||||||
|
return withErrorHandling(async () => {
|
||||||
|
const session = getSession(req);
|
||||||
|
if (!session) {
|
||||||
|
return errorResponse("Unauthorized", 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { userService } = await import("@shared/modules/user/user.service");
|
||||||
|
const user = await userService.getUserById(session.discordId);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return errorResponse("User not found", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResponse(user);
|
||||||
|
}, "fetch current user");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathname === "/api/me/inventory" && method === "GET") {
|
||||||
|
return withErrorHandling(async () => {
|
||||||
|
const session = getSession(req);
|
||||||
|
if (!session) {
|
||||||
|
return errorResponse("Unauthorized", 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { inventoryService } = await import("@shared/modules/inventory/inventory.service");
|
||||||
|
const inventory = await inventoryService.getInventory(session.discordId);
|
||||||
|
return jsonResponse({ inventory });
|
||||||
|
}, "fetch current user inventory");
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,12 +89,12 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
const { users } = await import("@shared/db/schema");
|
const { users } = await import("@shared/db/schema");
|
||||||
const { DrizzleClient } = await import("@shared/db/DrizzleClient");
|
const { DrizzleClient } = await import("@shared/db/DrizzleClient");
|
||||||
const { ilike, desc, asc, sql } = await import("drizzle-orm");
|
const { ilike, desc, asc, sql } = await import("drizzle-orm");
|
||||||
|
const queryParams = parseQuery(url, UserQuerySchema);
|
||||||
|
if (queryParams instanceof Response) {
|
||||||
|
return queryParams;
|
||||||
|
}
|
||||||
|
|
||||||
const search = url.searchParams.get("search") || undefined;
|
const { search, sortBy, sortOrder, limit, offset } = queryParams;
|
||||||
const sortBy = url.searchParams.get("sortBy") || "balance";
|
|
||||||
const sortOrder = url.searchParams.get("sortOrder") || "desc";
|
|
||||||
const limit = url.searchParams.get("limit") ? parseInt(url.searchParams.get("limit")!) : 50;
|
|
||||||
const offset = url.searchParams.get("offset") ? parseInt(url.searchParams.get("offset")!) : 0;
|
|
||||||
|
|
||||||
let query = DrizzleClient.select().from(users);
|
let query = DrizzleClient.select().from(users);
|
||||||
|
|
||||||
@@ -146,7 +180,10 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
|
|
||||||
return withErrorHandling(async () => {
|
return withErrorHandling(async () => {
|
||||||
const { userService } = await import("@shared/modules/user/user.service");
|
const { userService } = await import("@shared/modules/user/user.service");
|
||||||
const data = await req.json() as Record<string, any>;
|
const parsed = await parseBody(req, UpdateUserSchema);
|
||||||
|
if (parsed instanceof Response) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
const existing = await userService.getUserById(id);
|
const existing = await userService.getUserById(id);
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
@@ -155,14 +192,16 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
|
|
||||||
// Build update data (only allow safe fields)
|
// Build update data (only allow safe fields)
|
||||||
const updateData: any = {};
|
const updateData: any = {};
|
||||||
if (data.username !== undefined) updateData.username = data.username;
|
if (parsed.username !== undefined) updateData.username = parsed.username;
|
||||||
if (data.balance !== undefined) updateData.balance = BigInt(data.balance);
|
if (parsed.balance !== undefined) updateData.balance = BigInt(parsed.balance);
|
||||||
if (data.xp !== undefined) updateData.xp = BigInt(data.xp);
|
if (parsed.xp !== undefined) updateData.xp = BigInt(parsed.xp);
|
||||||
if (data.level !== undefined) updateData.level = parseInt(data.level);
|
if (parsed.level !== undefined) updateData.level = parsed.level;
|
||||||
if (data.dailyStreak !== undefined) updateData.dailyStreak = parseInt(data.dailyStreak);
|
if (parsed.dailyStreak !== undefined) updateData.dailyStreak = parsed.dailyStreak;
|
||||||
if (data.isActive !== undefined) updateData.isActive = Boolean(data.isActive);
|
if (parsed.isActive !== undefined) updateData.isActive = parsed.isActive;
|
||||||
if (data.settings !== undefined) updateData.settings = data.settings;
|
if (parsed.settings !== undefined) updateData.settings = parsed.settings;
|
||||||
if (data.classId !== undefined) updateData.classId = BigInt(data.classId);
|
if (parsed.classId !== undefined) {
|
||||||
|
updateData.classId = parsed.classId === null ? null : BigInt(parsed.classId);
|
||||||
|
}
|
||||||
|
|
||||||
const updatedUser = await userService.updateUser(id, updateData);
|
const updatedUser = await userService.updateUser(id, updateData);
|
||||||
return jsonResponse({ success: true, user: updatedUser });
|
return jsonResponse({ success: true, user: updatedUser });
|
||||||
@@ -215,13 +254,12 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
|
|
||||||
return withErrorHandling(async () => {
|
return withErrorHandling(async () => {
|
||||||
const { inventoryService } = await import("@shared/modules/inventory/inventory.service");
|
const { inventoryService } = await import("@shared/modules/inventory/inventory.service");
|
||||||
const data = await req.json() as Record<string, any>;
|
const parsed = await parseBody(req, InventoryAddSchema);
|
||||||
|
if (parsed instanceof Response) {
|
||||||
if (!data.itemId || !data.quantity) {
|
return parsed;
|
||||||
return errorResponse("Missing required fields: itemId, quantity", 400);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = await inventoryService.addItem(id, data.itemId, BigInt(data.quantity));
|
const entry = await inventoryService.addItem(id, parsed.itemId, BigInt(parsed.quantity));
|
||||||
return jsonResponse({ success: true, entry }, 201);
|
return jsonResponse({ success: true, entry }, 201);
|
||||||
}, "add item to inventory");
|
}, "add item to inventory");
|
||||||
}
|
}
|
||||||
@@ -245,11 +283,12 @@ async function handler(ctx: RouteContext): Promise<Response | null> {
|
|||||||
|
|
||||||
return withErrorHandling(async () => {
|
return withErrorHandling(async () => {
|
||||||
const { inventoryService } = await import("@shared/modules/inventory/inventory.service");
|
const { inventoryService } = await import("@shared/modules/inventory/inventory.service");
|
||||||
|
const queryParams = parseQuery(url, InventoryRemoveQuerySchema);
|
||||||
|
if (queryParams instanceof Response) {
|
||||||
|
return queryParams;
|
||||||
|
}
|
||||||
|
|
||||||
const amount = url.searchParams.get("amount");
|
await inventoryService.removeItem(userId, itemId, BigInt(queryParams.amount));
|
||||||
const quantity = amount ? BigInt(amount) : 1n;
|
|
||||||
|
|
||||||
await inventoryService.removeItem(userId, itemId, quantity);
|
|
||||||
return new Response(null, { status: 204 });
|
return new Response(null, { status: 204 });
|
||||||
}, "remove item from inventory");
|
}, "remove item from inventory");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,8 +135,7 @@ mock.module("@shared/lib/utils", () => ({
|
|||||||
// --- Mock Auth (bypass authentication) ---
|
// --- Mock Auth (bypass authentication) ---
|
||||||
mock.module("./routes/auth.routes", () => ({
|
mock.module("./routes/auth.routes", () => ({
|
||||||
authRoutes: { name: "auth", handler: () => null },
|
authRoutes: { name: "auth", handler: () => null },
|
||||||
isAuthenticated: () => true,
|
getSession: () => ({ discordId: "123", username: "testuser", role: "admin", expiresAt: Date.now() + 3600000 }),
|
||||||
getSession: () => ({ discordId: "123", username: "testuser", expiresAt: Date.now() + 3600000 }),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// --- Mock Logger ---
|
// --- Mock Logger ---
|
||||||
|
|||||||
@@ -113,8 +113,7 @@ mock.module("bun", () => {
|
|||||||
// Mock auth (bypass authentication)
|
// Mock auth (bypass authentication)
|
||||||
mock.module("./routes/auth.routes", () => ({
|
mock.module("./routes/auth.routes", () => ({
|
||||||
authRoutes: { name: "auth", handler: () => null },
|
authRoutes: { name: "auth", handler: () => null },
|
||||||
isAuthenticated: () => true,
|
getSession: () => ({ discordId: "123", username: "testuser", role: "admin", expiresAt: Date.now() + 3600000 }),
|
||||||
getSession: () => ({ discordId: "123", username: "testuser", expiresAt: Date.now() + 3600000 }),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Import createWebServer after mocks
|
// Import createWebServer after mocks
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, test, expect, afterAll, mock } from "bun:test";
|
import { beforeEach, describe, test, expect, afterAll, mock } from "bun:test";
|
||||||
import type { WebServerInstance } from "./server";
|
import type { WebServerInstance } from "./server";
|
||||||
|
|
||||||
interface MockBotStats {
|
interface MockBotStats {
|
||||||
@@ -66,11 +66,17 @@ mock.module("@shared/lib/config", () => ({
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 4. Mock auth (bypass authentication for testing)
|
let currentSession: { discordId: string; username: string; role: "admin" | "player"; expiresAt: number } | null = {
|
||||||
|
discordId: "123",
|
||||||
|
username: "admin-user",
|
||||||
|
role: "admin",
|
||||||
|
expiresAt: Date.now() + 3600000,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. Mock auth with a mutable session so tests can exercise authz paths.
|
||||||
mock.module("./routes/auth.routes", () => ({
|
mock.module("./routes/auth.routes", () => ({
|
||||||
authRoutes: { name: "auth", handler: () => null },
|
authRoutes: { name: "auth", handler: () => null },
|
||||||
isAuthenticated: () => true,
|
getSession: () => currentSession,
|
||||||
getSession: () => ({ discordId: "123", username: "testuser", expiresAt: Date.now() + 3600000 }),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 5. Mock BotClient (used by stats helper for maintenanceMode)
|
// 5. Mock BotClient (used by stats helper for maintenanceMode)
|
||||||
@@ -91,37 +97,55 @@ describe("WebServer Security & Limits", () => {
|
|||||||
const hostname = "127.0.0.1";
|
const hostname = "127.0.0.1";
|
||||||
let serverInstance: WebServerInstance | null = null;
|
let serverInstance: WebServerInstance | null = null;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
currentSession = {
|
||||||
|
discordId: "123",
|
||||||
|
username: "admin-user",
|
||||||
|
role: "admin",
|
||||||
|
expiresAt: Date.now() + 3600000,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
if (serverInstance) {
|
if (serverInstance) {
|
||||||
await serverInstance.stop();
|
await serverInstance.stop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should reject more than 10 concurrent WebSocket connections", async () => {
|
test("should reject unauthorized websocket requests", async () => {
|
||||||
serverInstance = await createWebServer({ port, hostname });
|
serverInstance = await createWebServer({ port, hostname });
|
||||||
const wsUrl = `ws://${hostname}:${port}/ws`;
|
currentSession = null;
|
||||||
const sockets: WebSocket[] = [];
|
|
||||||
|
|
||||||
try {
|
const response = await fetch(`http://${hostname}:${port}/ws`);
|
||||||
// Attempt to open 12 connections (limit is 10)
|
const body = await response.text();
|
||||||
for (let i = 0; i < 12; i++) {
|
|
||||||
const ws = new WebSocket(wsUrl);
|
|
||||||
sockets.push(ws);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 5));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give connections time to settle
|
expect(response.status).toBe(401);
|
||||||
await new Promise(resolve => setTimeout(resolve, 800));
|
expect(body).toBe("Unauthorized");
|
||||||
|
});
|
||||||
|
|
||||||
const pendingCount = serverInstance.server.pendingWebSockets;
|
test("should accept websocket requests for authenticated sessions", async () => {
|
||||||
expect(pendingCount).toBeLessThanOrEqual(10);
|
if (!serverInstance) {
|
||||||
} finally {
|
serverInstance = await createWebServer({ port, hostname });
|
||||||
sockets.forEach(s => {
|
|
||||||
if (s.readyState === WebSocket.OPEN || s.readyState === WebSocket.CONNECTING) {
|
|
||||||
s.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ws = new WebSocket(`ws://${hostname}:${port}/ws`);
|
||||||
|
const opened = await new Promise<boolean>((resolve) => {
|
||||||
|
const timeout = setTimeout(() => resolve(false), 1000);
|
||||||
|
ws.addEventListener("open", () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
|
ws.addEventListener("error", () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
||||||
|
ws.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(opened).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should return 200 for health check", async () => {
|
test("should return 200 for health check", async () => {
|
||||||
@@ -135,15 +159,30 @@ describe("WebServer Security & Limits", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Administrative Actions", () => {
|
describe("Administrative Actions", () => {
|
||||||
test("should allow administrative actions without token", async () => {
|
test("should allow administrative actions for admin sessions", async () => {
|
||||||
const response = await fetch(`http://${hostname}:${port}/api/actions/reload-commands`, {
|
const response = await fetch(`http://${hostname}:${port}/api/actions/reload-commands`, {
|
||||||
method: "POST"
|
method: "POST"
|
||||||
});
|
});
|
||||||
// Should be 200 (OK) or 500 (if underlying service fails, but NOT 401)
|
|
||||||
expect(response.status).not.toBe(401);
|
|
||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should reject administrative actions for player sessions", async () => {
|
||||||
|
currentSession = {
|
||||||
|
discordId: "456",
|
||||||
|
username: "player-user",
|
||||||
|
role: "player",
|
||||||
|
expiresAt: Date.now() + 3600000,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(`http://${hostname}:${port}/api/actions/reload-commands`, {
|
||||||
|
method: "POST"
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(403);
|
||||||
|
const data = await response.json() as { error: string };
|
||||||
|
expect(data.error).toBe("Admin access required");
|
||||||
|
});
|
||||||
|
|
||||||
test("should reject maintenance mode with invalid payload", async () => {
|
test("should reject maintenance mode with invalid payload", async () => {
|
||||||
const response = await fetch(`http://${hostname}:${port}/api/actions/maintenance-mode`, {
|
const response = await fetch(`http://${hostname}:${port}/api/actions/maintenance-mode`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ import type { WsConnectionData } from "./games/GameServer";
|
|||||||
import { getSession } from "./routes/auth.routes";
|
import { getSession } from "./routes/auth.routes";
|
||||||
import { GameWsClientSchema } from "./games/types";
|
import { GameWsClientSchema } from "./games/types";
|
||||||
|
|
||||||
|
// Register game plugins
|
||||||
|
import { gameRegistry } from "@shared/games/registry";
|
||||||
|
import { chessPlugin } from "@shared/games/chess/chess.plugin";
|
||||||
|
import { blackjackPlugin } from "@shared/games/blackjack/blackjack.plugin";
|
||||||
|
gameRegistry.register(chessPlugin);
|
||||||
|
gameRegistry.register(blackjackPlugin);
|
||||||
|
|
||||||
const WS_CONFIG = {
|
const WS_CONFIG = {
|
||||||
MAX_CONNECTIONS: 200,
|
MAX_CONNECTIONS: 200,
|
||||||
MAX_PAYLOAD_BYTES: 16384,
|
MAX_PAYLOAD_BYTES: 16384,
|
||||||
@@ -176,7 +183,9 @@ export async function createWebServer(config: WebServerConfig = {}): Promise<Web
|
|||||||
// Route game messages — try to parse as a game client message
|
// Route game messages — try to parse as a game client message
|
||||||
const gameCheck = GameWsClientSchema.safeParse(rawData);
|
const gameCheck = GameWsClientSchema.safeParse(rawData);
|
||||||
if (gameCheck.success) {
|
if (gameCheck.success) {
|
||||||
gameServer.handleMessage(ws, rawData);
|
gameServer.handleMessage(ws, rawData).catch(err =>
|
||||||
|
logger.error("web", `Game message handler error: ${err}`),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,18 +240,3 @@ export async function createWebServer(config: WebServerConfig = {}): Promise<Web
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the web server from the main application root.
|
|
||||||
* Kept for backward compatibility.
|
|
||||||
*
|
|
||||||
* @param webProjectPath - Deprecated, no longer used
|
|
||||||
* @param config - Server configuration options
|
|
||||||
* @returns Promise resolving to server instance
|
|
||||||
*/
|
|
||||||
export async function startWebServerFromRoot(
|
|
||||||
webProjectPath: string,
|
|
||||||
config: WebServerConfig = {}
|
|
||||||
): Promise<WebServerInstance> {
|
|
||||||
return createWebServer(config);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { AuroraClient } from "@/lib/BotClient";
|
import { AuroraClient } from "@/lib/BotClient";
|
||||||
import { env } from "@shared/lib/env";
|
import { env } from "@shared/lib/env";
|
||||||
import { join } from "node:path";
|
|
||||||
import { initializeConfig } from "@shared/lib/config";
|
import { initializeConfig } from "@shared/lib/config";
|
||||||
import { registerDomainEventListeners } from "@shared/lib/eventWiring";
|
import { registerDomainEventListeners } from "@shared/lib/eventWiring";
|
||||||
import { startWebServerFromRoot } from "../api/src/server";
|
import { createWebServer } from "../api/src/server";
|
||||||
|
|
||||||
// Initialize config from database
|
// Initialize config from database
|
||||||
await initializeConfig();
|
await initializeConfig();
|
||||||
@@ -21,12 +20,11 @@ console.log("🌐 Starting web server...");
|
|||||||
|
|
||||||
let shuttingDown = false;
|
let shuttingDown = false;
|
||||||
|
|
||||||
const webProjectPath = join(import.meta.dir, "../api");
|
|
||||||
const webPort = Number(process.env.WEB_PORT) || 3000;
|
const webPort = Number(process.env.WEB_PORT) || 3000;
|
||||||
const webHost = process.env.HOST || "0.0.0.0";
|
const webHost = process.env.HOST || "0.0.0.0";
|
||||||
|
|
||||||
// Start web server in the same process
|
// Start web server in the same process
|
||||||
const webServer = await startWebServerFromRoot(webProjectPath, {
|
const webServer = await createWebServer({
|
||||||
port: webPort,
|
port: webPort,
|
||||||
hostname: webHost,
|
hostname: webHost,
|
||||||
});
|
});
|
||||||
@@ -53,4 +51,4 @@ const shutdownHandler = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
process.on("SIGINT", shutdownHandler);
|
process.on("SIGINT", shutdownHandler);
|
||||||
process.on("SIGTERM", shutdownHandler);
|
process.on("SIGTERM", shutdownHandler);
|
||||||
|
|||||||
@@ -163,8 +163,9 @@ export function getShopListingMessage(
|
|||||||
|
|
||||||
if (line) {
|
if (line) {
|
||||||
if (!tiers[rarity]) tiers[rarity] = { items: [], totalChance: 0 };
|
if (!tiers[rarity]) tiers[rarity] = { items: [], totalChance: 0 };
|
||||||
tiers[rarity].items.push(line);
|
const tier = tiers[rarity]!;
|
||||||
tiers[rarity].totalChance += chance;
|
tier.items.push(line);
|
||||||
|
tier.totalChance += chance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,116 +1,38 @@
|
|||||||
# Trivia - Components v2 Implementation
|
# Trivia UI
|
||||||
|
|
||||||
This trivia feature uses **Discord Components v2** for a premium visual experience.
|
The trivia command uses Discord Components v2 for the question, result, and timeout states.
|
||||||
|
|
||||||
## 🎨 Visual Features
|
## Files
|
||||||
|
|
||||||
### **Container with Accent Colors**
|
```text
|
||||||
Each trivia question is displayed in a Container with a colored accent bar that changes based on difficulty:
|
|
||||||
- **🟢 Easy**: Green accent bar (`0x57F287`)
|
|
||||||
- **🟡 Medium**: Yellow accent bar (`0xFEE75C`)
|
|
||||||
- **🔴 Hard**: Red accent bar (`0xED4245`)
|
|
||||||
|
|
||||||
### **Modern Layout Components**
|
|
||||||
- **TextDisplay** - Rich markdown formatting for question text
|
|
||||||
- **Separator** - Visual spacing between sections
|
|
||||||
- **Container** - Groups all content with difficulty-based styling
|
|
||||||
|
|
||||||
### **Interactive Features**
|
|
||||||
✅ **Give Up Button** - Players can forfeit if they're unsure
|
|
||||||
✅ **Disabled Answer Buttons** - After answering, buttons show:
|
|
||||||
- ✅ Green for correct answer
|
|
||||||
- ❌ Red for user's incorrect answer
|
|
||||||
- Gray for other options
|
|
||||||
|
|
||||||
✅ **Time Display** - Shows both relative time (`in 30s`) and seconds remaining
|
|
||||||
✅ **Stakes Preview** - Clear display: `50 AU ➜ 100 AU`
|
|
||||||
|
|
||||||
## 📁 File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
bot/modules/trivia/
|
bot/modules/trivia/
|
||||||
├── trivia.view.ts # Components v2 view functions
|
trivia.types.ts
|
||||||
├── trivia.interaction.ts # Button interaction handler
|
trivia.view.ts
|
||||||
└── README.md # This file
|
trivia.interaction.ts
|
||||||
|
|
||||||
bot/commands/economy/
|
bot/commands/economy/trivia.ts
|
||||||
└── trivia.ts # /trivia slash command
|
shared/modules/trivia/trivia.service.ts
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔧 Technical Details
|
## What the view layer does
|
||||||
|
|
||||||
### Components v2 Requirements
|
- renders the active question as a Components v2 container
|
||||||
- Uses `MessageFlags.IsComponentsV2` flag
|
- colors the container by difficulty
|
||||||
- No `embeds` or `content` fields (uses TextDisplay instead)
|
- renders answer buttons from the session's shuffled answers
|
||||||
- Numeric component types:
|
- renders separate result and timeout views with disabled buttons
|
||||||
- `1` - Action Row
|
|
||||||
- `2` - Button
|
|
||||||
- `10` - Text Display
|
|
||||||
- `14` - Separator
|
|
||||||
- `17` - Container
|
|
||||||
- Max 40 components per message (vs 5 for legacy)
|
|
||||||
|
|
||||||
### Button Styles
|
## Current interaction flow
|
||||||
- **Secondary (2)**: Gray - Used for answer buttons
|
|
||||||
- **Success (3)**: Green - Used for "True" and correct answers
|
|
||||||
- **Danger (4)**: Red - Used for "False", incorrect answers, and "Give Up"
|
|
||||||
|
|
||||||
## 🎮 User Experience Flow
|
1. `/trivia` checks cooldown before deferring.
|
||||||
|
2. `startTrivia()` deducts the entry fee and creates the session.
|
||||||
|
3. `getTriviaQuestionView()` renders the prompt.
|
||||||
|
4. `trivia.interaction.ts` compares the clicked answer with the session's `correctIndex`.
|
||||||
|
5. `submitAnswer()` finalizes the session and the view swaps to success, failure, or timeout output.
|
||||||
|
|
||||||
1. User runs `/trivia`
|
The command also schedules a timeout cleanup with a 5-second grace period after `config.trivia.timeoutSeconds`.
|
||||||
2. Sees question in a Container with difficulty-based accent color
|
|
||||||
3. Can choose to:
|
|
||||||
- Select an answer (A/B/C/D or True/False)
|
|
||||||
- Give up using the 🏳️ button
|
|
||||||
4. After answering, sees result with:
|
|
||||||
- Disabled buttons showing correct/incorrect answers
|
|
||||||
- Container with result-based accent color (green/red/yellow)
|
|
||||||
- Reward or penalty information
|
|
||||||
|
|
||||||
## 🌟 Visual Examples
|
## Custom IDs
|
||||||
|
|
||||||
### Question Display
|
- answer buttons: `TRIVIA_CUSTOM_IDS.ANSWER(sessionId, index)`
|
||||||
```
|
- give up: `TRIVIA_CUSTOM_IDS.GIVE_UP(sessionId)`
|
||||||
┌─[GREEN]─────────────────────────┐
|
- result/timeout buttons use non-interactive result IDs
|
||||||
│ # 🎯 Trivia Challenge │
|
|
||||||
│ 🟢 Easy • 📚 Geography │
|
|
||||||
│ ─────────────────────────── │
|
|
||||||
│ ### What is the capital of │
|
|
||||||
│ France? │
|
|
||||||
│ │
|
|
||||||
│ ⏱️ Time: in 30s (30s) │
|
|
||||||
│ 💰 Stakes: 50 AU ➜ 100 AU │
|
|
||||||
│ 👤 Player: Username │
|
|
||||||
└─────────────────────────────────┘
|
|
||||||
[🇦 A: Paris] [🇧 B: London]
|
|
||||||
[🇨 C: Berlin] [🇩 D: Madrid]
|
|
||||||
[🏳️ Give Up]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Result Display (Correct)
|
|
||||||
```
|
|
||||||
┌─[GREEN]─────────────────────────┐
|
|
||||||
│ # 🎉 Correct Answer! │
|
|
||||||
│ ### What is the capital of │
|
|
||||||
│ France? │
|
|
||||||
│ ─────────────────────────── │
|
|
||||||
│ ✅ Your answer: Paris │
|
|
||||||
│ │
|
|
||||||
│ 💰 Reward: +100 AU │
|
|
||||||
│ │
|
|
||||||
│ 🏆 Great job! Keep it up! │
|
|
||||||
└─────────────────────────────────┘
|
|
||||||
[✅ A: Paris] [❌ B: London]
|
|
||||||
[❌ C: Berlin] [❌ D: Madrid]
|
|
||||||
(all buttons disabled)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 Future Enhancements
|
|
||||||
|
|
||||||
Potential improvements:
|
|
||||||
- [ ] Thumbnail images based on trivia category
|
|
||||||
- [ ] Progress bar for time remaining
|
|
||||||
- [ ] Streak counter display
|
|
||||||
- [ ] Category-specific accent colors
|
|
||||||
- [ ] Media Gallery for image-based questions
|
|
||||||
- [ ] Leaderboard integration in results
|
|
||||||
|
|||||||
15
bun.lock
@@ -6,6 +6,7 @@
|
|||||||
"name": "app",
|
"name": "app",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@napi-rs/canvas": "^0.1.89",
|
"@napi-rs/canvas": "^0.1.89",
|
||||||
|
"chess.js": "^1.4.0",
|
||||||
"discord.js": "^14.25.1",
|
"discord.js": "^14.25.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"drizzle-orm": "^0.44.7",
|
"drizzle-orm": "^0.44.7",
|
||||||
@@ -26,10 +27,12 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@imgly/background-removal": "^1.7.0",
|
"@imgly/background-removal": "^1.7.0",
|
||||||
|
"chess.js": "^1.4.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.564.0",
|
"lucide-react": "^0.564.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
|
"react-chessboard": "^5.10.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-router-dom": "^7.13.2",
|
"react-router-dom": "^7.13.2",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
@@ -99,6 +102,14 @@
|
|||||||
|
|
||||||
"@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
|
"@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
|
||||||
|
|
||||||
|
"@dnd-kit/accessibility": ["@dnd-kit/accessibility@3.1.1", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw=="],
|
||||||
|
|
||||||
|
"@dnd-kit/core": ["@dnd-kit/core@6.3.1", "", { "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ=="],
|
||||||
|
|
||||||
|
"@dnd-kit/modifiers": ["@dnd-kit/modifiers@9.0.0", "", { "dependencies": { "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@dnd-kit/core": "^6.3.0", "react": ">=16.8.0" } }, "sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw=="],
|
||||||
|
|
||||||
|
"@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="],
|
||||||
|
|
||||||
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
||||||
|
|
||||||
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
||||||
@@ -337,6 +348,8 @@
|
|||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="],
|
"caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="],
|
||||||
|
|
||||||
|
"chess.js": ["chess.js@1.4.0", "", {}, "sha512-BBJgrrtKQOzFLonR0l+k64A98NLemPwNsCskwb+29bRwobUa4iTm51E1kwGPbWXAcfdDa18nad6vpPPKPWarqw=="],
|
||||||
|
|
||||||
"class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
|
"class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
|
||||||
|
|
||||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||||
@@ -473,6 +486,8 @@
|
|||||||
|
|
||||||
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
||||||
|
|
||||||
|
"react-chessboard": ["react-chessboard@5.10.0", "", { "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/modifiers": "^9.0.0" }, "peerDependencies": { "react": "^19.0.0", "react-dom": "^19.0.0" } }, "sha512-Y3PgaCVhnDG3IaQfu86OzTSEIEAUtuU5XwmHWnx3tcFOX7lSoAq81ZFX3MBj6y5a6FzDMTczMVmkkrV2CzTrIw=="],
|
||||||
|
|
||||||
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
|
"react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
|
||||||
|
|
||||||
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
||||||
|
|||||||
@@ -1,84 +1,60 @@
|
|||||||
# Design System Specification: Stellar Editorial
|
# Stellar Editorial
|
||||||
|
|
||||||
## 1. Overview & Creative North Star: "The Celestial Curator"
|
This document now tracks the design language that is actually implemented in the panel, with a small amount of forward-looking guidance where the code is still catching up.
|
||||||
This design system is built to evoke the atmosphere of an elite, secretive astronomical academy. We are moving away from the "utility-first" aesthetic of standard Discord bots toward a "Digital Editorial" experience.
|
|
||||||
|
|
||||||
**Creative North Star: The Celestial Curator**
|
## Current implementation status
|
||||||
The UI should feel like a high-end, leather-bound astronomical ledger reimagined for the 22nd century. We achieve this through "The Void & The Light"—using deep, expansive dark spaces contrasted against precise, shimmering accents. We reject the rigid, boxed-in layouts of traditional dashboards in favor of intentional asymmetry, overlapping layers, and a profound sense of depth. Every element should feel curated, prestigious, and slightly mysterious.
|
|
||||||
|
|
||||||
---
|
Implemented today in `panel/src/index.css` and the active panel pages:
|
||||||
|
|
||||||
## 2. Colors: The Palette of the Night Sky
|
- dark "void and light" surface stack
|
||||||
The color philosophy is rooted in "Atmospheric Depth." We use a hierarchy of midnight tones to create a sense of infinite space, punctuated by gold and starlight.
|
- Celestial Gold primary color family
|
||||||
|
- Noto Serif, Manrope, Space Grotesk, and JetBrains Mono typography
|
||||||
|
- low-contrast "ghost border" treatment
|
||||||
|
- rounded surface hierarchy for cards, sidebars, and controls
|
||||||
|
|
||||||
### The "No-Line" Rule
|
Partially implemented or still aspirational:
|
||||||
**Explicit Instruction:** Designers are prohibited from using 1px solid borders to define sections or containers. Layout boundaries must be defined exclusively through background color shifts. Use `surface-container-low` against a `surface` background to create a subtle "carved" effect, or `surface-container-high` to create "lift."
|
|
||||||
|
|
||||||
### Surface Hierarchy & Nesting
|
- stronger asymmetry and editorial layouts across more pages
|
||||||
Instead of a flat grid, treat the UI as a series of nested celestial bodies.
|
- decorative constellation/nebula treatments
|
||||||
* **Base Layer:** `surface` (#0d1323) – The infinite void.
|
- more consistent premium component states across every admin screen
|
||||||
* **Secondary Sections:** `surface-container-low` (#151b2c) – Softly recessed areas.
|
|
||||||
* **Interactive Cards:** `surface-container-high` (#24293b) – Elevated content.
|
|
||||||
* **Floating Modals:** `surface-container-highest` (#2f3446) – The closest layer to the viewer.
|
|
||||||
|
|
||||||
### The "Glass & Gradient" Rule
|
## Design intent
|
||||||
To capture the "Stellar Academy" prestige, use Glassmorphism for floating elements.
|
|
||||||
* **Formula:** Apply `surface-variant` at 40% opacity with a `24px` backdrop blur.
|
|
||||||
* **Signature Textures:** For primary CTAs and hero headers, utilize a linear gradient: `primary` (#e9c349) to `primary-fixed-dim` (#e9c349) at a 135-degree angle. This provides a "metallic brass" sheen that flat colors cannot replicate.
|
|
||||||
|
|
||||||
---
|
Aurora's panel should feel like an elite astronomical academy rather than a default admin dashboard. The codebase already follows that direction through the theme tokens and typography system; new UI work should continue that tone instead of falling back to generic SaaS styling.
|
||||||
|
|
||||||
## 3. Typography: Academic Authority
|
## Core tokens
|
||||||
The type system pairs the intellectual weight of a classic Serif with the technical precision of a modern Sans-Serif.
|
|
||||||
|
|
||||||
* **Display & Headlines (Noto Serif):** These are the "Ancient Manuscripts." Use `display-lg` to `headline-sm` for titles and major headers. This font conveys the "Elite Academy" prestige.
|
Surface hierarchy:
|
||||||
* **UI & Body (Manrope):** This is the "Modern Faculty." Use `title-lg` down to `body-sm` for all functional UI elements, descriptions, and system feedback.
|
|
||||||
* **Technical Labels (Space Grotesk):** Use `label-md` and `label-sm` for status tags, metadata, and numerical RPG stats. This adds a subtle "astronomical instrument" feel.
|
|
||||||
|
|
||||||
**Editorial Tip:** Use exaggerated tracking (0.1em) on `label-sm` in all-caps to enhance the high-end, premium feel.
|
- `--color-background`: `#0d1323`
|
||||||
|
- `--color-surface-container-low`: `#151b2c`
|
||||||
|
- `--color-surface-container-high`: `#24293b`
|
||||||
|
- `--color-surface-container-highest`: `#2f3446`
|
||||||
|
|
||||||
---
|
Primary accents:
|
||||||
|
|
||||||
## 4. Elevation & Depth: Tonal Layering
|
- `--color-primary`: `#e9c349`
|
||||||
We do not use structural lines. We use light and shadow to imply existence.
|
- `--color-primary-fixed-dim`: `#d4af37`
|
||||||
|
- `--color-primary-container`: `#3d2e00`
|
||||||
|
|
||||||
* **The Layering Principle:** Depth is achieved by stacking. Place a `surface-container-lowest` card on a `surface-container-low` section. This creates a "soft-edge" transition that feels organic rather than digital.
|
Typography:
|
||||||
* **Ambient Shadows:** For floating elements, use a shadow with a 40px blur, 0px offset, and 6% opacity using the `on-surface` color. This creates a "glow" rather than a "drop shadow," mimicking how light behaves in a dark vacuum.
|
|
||||||
* **The "Ghost Border" Fallback:** If a boundary is strictly required for accessibility, use a "Ghost Border": `outline-variant` (#45464c) at 15% opacity. Never use 100% opaque borders.
|
|
||||||
* **The Aurora Glow:** Use `primary-container` as a very large, soft radial gradient (600px+) behind key UI elements to create a "nebula" effect, grounding the component in space.
|
|
||||||
|
|
||||||
---
|
- display: Noto Serif
|
||||||
|
- body: Manrope
|
||||||
|
- labels: Space Grotesk
|
||||||
|
- mono: JetBrains Mono
|
||||||
|
|
||||||
## 5. Components: Precision & Prestige
|
## Component guidance
|
||||||
|
|
||||||
### Buttons
|
- Prefer tonal separation over heavy borders.
|
||||||
* **Primary:** A gradient-filled container (`primary` to `primary-fixed-dim`). No border. `label-md` (Space Grotesk) text in `on-primary`.
|
- Use gold as a focused accent, not a flood color.
|
||||||
* **Secondary:** A "Ghost Border" container with `secondary` text. Upon hover, the background fills with 5% `secondary` white.
|
- Keep text contrast high and metadata quieter.
|
||||||
* **Tertiary:** Text-only in `primary`, but with a small `2px` gold dot (star) preceding the label.
|
- Sidebar, cards, tables, and game views should feel like the same product family.
|
||||||
|
- Avoid plain white cards, Discord blurple defaults, and generic component-library styling.
|
||||||
|
|
||||||
### Cards & Lists
|
## Practical rules for future work
|
||||||
* **Rule:** Forbid the use of divider lines.
|
|
||||||
* **Separation:** Use `spacing-6` (2rem) of vertical white space to separate list items. If items must be grouped, use a subtle background shift to `surface-container-low`.
|
|
||||||
* **Visual Interest:** In the top-right corner of a "Highest" elevation card, place a subtle, 10% opacity constellation pattern SVG.
|
|
||||||
|
|
||||||
### Input Fields
|
- Start from the CSS tokens in `panel/src/index.css` instead of inventing new one-off colors.
|
||||||
* **Styling:** Inputs should not be boxes. Use a `surface-container-lowest` background with a `2px` bottom-only border in `outline-variant`.
|
- Preserve the current font roles unless there is a strong reason to change them.
|
||||||
* **Focus State:** The bottom border transitions to `primary` (gold) with a subtle `primary-container` outer glow.
|
- Use gradients, glow, or tonal depth sparingly and intentionally.
|
||||||
|
- Keep mobile behavior first-class; the existing layout already has mobile drawer behavior that new pages should respect.
|
||||||
### New Component: "The Astral Badge"
|
|
||||||
A specialized chip for RPG ranks.
|
|
||||||
* **Style:** `label-sm` text inside a `surface-container-highest` pill. It features a 1px "Ghost Border" and a tiny radial glow in the center-left using the `primary` color to represent a "guiding star."
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Do's and Don'ts
|
|
||||||
|
|
||||||
### Do:
|
|
||||||
* **Embrace Asymmetry:** Align text to the left but place decorative "constellation" elements floating on the right to break the grid.
|
|
||||||
* **Use Generous Spacing:** Premium design requires "breathing room." Use `spacing-10` and `spacing-12` for section margins.
|
|
||||||
* **Tint Your Greys:** Ensure all "neutrals" are pulled from the `secondary` and `tertiary` tokens, which are infused with midnight blue and silver.
|
|
||||||
|
|
||||||
### Don't:
|
|
||||||
* **Don't use pure black (#000):** It kills the "depth" of the midnight blue theme. Use `surface-container-lowest`.
|
|
||||||
* **Don't use standard Discord "Blurple":** This system must feel like a custom application that exists outside the standard Discord ecosystem.
|
|
||||||
* **Don't use hard corners:** Stick strictly to the `Roundedness Scale`. Most containers should use `xl` (0.75rem) or `lg` (0.5rem) to maintain a sophisticated, approachable feel.
|
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
"db:migrate-config": "docker compose run --rm app bun shared/scripts/migrate-config-to-db.ts",
|
"db:migrate-config": "docker compose run --rm app bun shared/scripts/migrate-config-to-db.ts",
|
||||||
"db:migrate-game-config": "docker compose run --rm app bun shared/scripts/migrate-game-settings-to-db.ts",
|
"db:migrate-game-config": "docker compose run --rm app bun shared/scripts/migrate-game-settings-to-db.ts",
|
||||||
"db:migrate-all": "docker compose run --rm app sh -c 'bun shared/scripts/migrate-config-to-db.ts && bun shared/scripts/migrate-game-settings-to-db.ts'",
|
"db:migrate-all": "docker compose run --rm app sh -c 'bun shared/scripts/migrate-config-to-db.ts && bun shared/scripts/migrate-game-settings-to-db.ts'",
|
||||||
"test": "bash shared/scripts/test-sequential.sh",
|
"test": "bash shared/scripts/test-isolated.sh",
|
||||||
"test:ci": "bash shared/scripts/test-sequential.sh --integration",
|
"test:ci": "bash shared/scripts/test-isolated.sh --integration",
|
||||||
"test:simulate-ci": "bash shared/scripts/simulate-ci.sh",
|
"test:simulate-ci": "bash shared/scripts/simulate-ci.sh",
|
||||||
"panel:dev": "cd panel && bun run dev",
|
"panel:dev": "cd panel && bun run dev",
|
||||||
"panel:build": "cd panel && bun run build",
|
"panel:build": "cd panel && bun run build",
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@napi-rs/canvas": "^0.1.89",
|
"@napi-rs/canvas": "^0.1.89",
|
||||||
|
"chess.js": "^1.4.0",
|
||||||
"discord.js": "^14.25.1",
|
"discord.js": "^14.25.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"drizzle-orm": "^0.44.7",
|
"drizzle-orm": "^0.44.7",
|
||||||
@@ -47,4 +48,4 @@
|
|||||||
"postgres": "^3.4.8",
|
"postgres": "^3.4.8",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
87
panel/AGENTS.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# Panel
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- React 19
|
||||||
|
- React Router 7
|
||||||
|
- Vite 6
|
||||||
|
- Tailwind CSS v4 via `@tailwindcss/vite`
|
||||||
|
- Local utilities: `clsx`, `tailwind-merge`, `class-variance-authority`
|
||||||
|
- Icons: `lucide-react`
|
||||||
|
|
||||||
|
The panel lives in `panel/src` and is built to `panel/dist`.
|
||||||
|
|
||||||
|
## Dev and runtime
|
||||||
|
|
||||||
|
- `bun run panel:dev` starts Vite on `http://localhost:5173`
|
||||||
|
- Vite proxies `/api`, `/auth`, `/assets`, and `/ws` to `http://localhost:3000`
|
||||||
|
- The Bun server serves the built panel from `panel/dist` in integrated mode
|
||||||
|
|
||||||
|
## Auth model
|
||||||
|
|
||||||
|
- `useAuth()` calls `GET /auth/me`
|
||||||
|
- unauthenticated users are sent through `/auth/discord`
|
||||||
|
- logout uses `POST /auth/logout`
|
||||||
|
- non-enrolled users see the `NotEnrolled` page
|
||||||
|
|
||||||
|
Roles:
|
||||||
|
|
||||||
|
- `admin`: admin routes plus player/game routes
|
||||||
|
- `player`: player/game routes only
|
||||||
|
|
||||||
|
## Active routes
|
||||||
|
|
||||||
|
- `/dashboard`
|
||||||
|
- `/leaderboards`
|
||||||
|
- `/games`
|
||||||
|
- `/:gameSlug/:roomId`
|
||||||
|
- `/admin`
|
||||||
|
- `/admin/users`
|
||||||
|
- `/admin/items`
|
||||||
|
- `/admin/classes`
|
||||||
|
- `/admin/quests`
|
||||||
|
- `/admin/lootdrops`
|
||||||
|
- `/admin/moderation`
|
||||||
|
- `/admin/transactions`
|
||||||
|
- `/admin/settings`
|
||||||
|
|
||||||
|
## Data layer
|
||||||
|
|
||||||
|
Shared hooks in `panel/src/lib`:
|
||||||
|
|
||||||
|
- `useAuth`
|
||||||
|
- `useDashboard`
|
||||||
|
- `useUsers`
|
||||||
|
- `useItems`
|
||||||
|
- `useSettings`
|
||||||
|
- `useWebSocket`
|
||||||
|
- `useGameRoom`
|
||||||
|
|
||||||
|
`panel/src/lib/api.ts` is a thin fetch wrapper:
|
||||||
|
|
||||||
|
- base path is empty because the panel is usually same-origin with the Bun server
|
||||||
|
- 401 triggers a redirect back into `/auth/discord`
|
||||||
|
- 204 and empty responses return `undefined`
|
||||||
|
|
||||||
|
## WebSocket and games
|
||||||
|
|
||||||
|
- `useWebSocket()` keeps a singleton browser WebSocket connection
|
||||||
|
- reconnects with exponential backoff up to 30 seconds
|
||||||
|
- `useGameRoom()` multiplexes room traffic over that shared socket
|
||||||
|
- current built-in game UIs are chess and blackjack
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
|
||||||
|
- Theme tokens live in `panel/src/index.css`
|
||||||
|
- Fonts currently loaded:
|
||||||
|
- Noto Serif
|
||||||
|
- Manrope
|
||||||
|
- Space Grotesk
|
||||||
|
- JetBrains Mono
|
||||||
|
- The implemented visual system is the "Stellar Editorial" direction documented in `docs/new-design/DESIGN.md`
|
||||||
|
|
||||||
|
## Current patterns
|
||||||
|
|
||||||
|
- Admin pages tend to use explicit `refetch()` after mutations instead of a shared cache layer
|
||||||
|
- Search inputs use a 300 ms debounce in hooks such as `useUsers()` and `useItems()`
|
||||||
|
- Layout and sidebar ownership lives in `panel/src/components/Layout.tsx`
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# Panel (Admin Dashboard)
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
- React 19 + React Router v7 + Tailwind CSS v4 (Vite plugin) + Lucide icons.
|
|
||||||
- No component library (no Radix, no shadcn). All styling is inline Tailwind.
|
|
||||||
- No external state management. All state via custom hooks (`useAuth`, `useUsers`, `useItems`, `useDashboard`, `useGameRoom`).
|
|
||||||
|
|
||||||
## API Client
|
|
||||||
- Thin `fetch` wrapper in `src/lib/api.ts` with typed generics: `get`, `post`, `put`, `del`.
|
|
||||||
- Credentials: `same-origin` only — CORS requests will fail.
|
|
||||||
- 401 responses redirect to Discord auth. No retry logic.
|
|
||||||
- 204 / empty responses return `undefined`.
|
|
||||||
|
|
||||||
## WebSocket
|
|
||||||
- Singleton pattern in `useWebSocket()`. Global handler Set — multiple hooks share one connection.
|
|
||||||
- Auto-reconnects with exponential backoff (max 30s).
|
|
||||||
- `send()` and `subscribe()` API for components.
|
|
||||||
|
|
||||||
## Patterns
|
|
||||||
- **Draft editing:** Pages use `selectedUser → userDraft` flow. Changes aren't auto-saved. `saveDraft()` commits, `discardDraft()` reverts, `isDirty()` detects changes.
|
|
||||||
- **Debounced search:** 300ms debounce on search inputs.
|
|
||||||
- **No cache invalidation:** After mutations, manually call `refetch()` to update lists.
|
|
||||||
|
|
||||||
## Routing
|
|
||||||
- Role-based: player routes (`/dashboard`, `/games`, `/leaderboards`) and admin routes (`/admin/*`).
|
|
||||||
- Sidebar auto-hides admin routes for non-admins.
|
|
||||||
|
|
||||||
## Theme
|
|
||||||
- Dark theme with "Celestial Gold" primary (`#e9c349`).
|
|
||||||
- Semantic colors: destructive, success, warning, info.
|
|
||||||
- 4-tier surface hierarchy. Utility: `cn()` from `clsx + tailwind-merge`.
|
|
||||||
@@ -10,10 +10,12 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@imgly/background-removal": "^1.7.0",
|
"@imgly/background-removal": "^1.7.0",
|
||||||
|
"chess.js": "^1.4.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.564.0",
|
"lucide-react": "^0.564.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
|
"react-chessboard": "^5.10.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-router-dom": "^7.13.2",
|
"react-router-dom": "^7.13.2",
|
||||||
"tailwind-merge": "^3.4.0",
|
"tailwind-merge": "^3.4.0",
|
||||||
|
|||||||
281
panel/public/cards/10_of_clubs.svg
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="10_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/10_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="117.62976"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 57.572834,25.099947 c 0,0 5.967372,-4.773898 5.967372,-11.392027 0,-3.8743954 -3.43972,-10.3065945 -11.392028,-10.3065945 -7.952308,0 -11.392028,6.4347116 -11.392028,10.3065945 0,6.618129 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163348 -18.444833,-1.638201 -18.444833,8.680956 0,5.16586 4.22113,10.849311 10.849311,10.849311 7.952308,0 11.392027,-8.680956 11.392027,-8.680956 0,0 1.010056,9.894531 -4.881939,15.191045 h 13.020178 c -5.891994,-5.294001 -4.881938,-15.191045 -4.881938,-15.191045 0,0 3.439718,8.680956 11.392027,8.680956 6.630693,0 10.849311,-5.685963 10.849311,-10.849311 0,-10.319157 -11.816654,-13.844304 -18.444833,-8.680956 z"
|
||||||
|
id="cl-9-8" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 57.110434,93.200747 c 0,0 5.967372,-4.773898 5.967372,-11.392027 0,-3.874396 -3.43972,-10.306594 -11.392028,-10.306594 -7.952308,0 -11.392028,6.434711 -11.392028,10.306594 0,6.618129 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163348 -18.444833,-1.638201 -18.444833,8.680953 0,5.16587 4.22113,10.84932 10.849311,10.84932 7.952308,0 11.392027,-8.68096 11.392027,-8.68096 0,0 1.010056,9.89453 -4.881939,15.19104 h 13.020178 c -5.891994,-5.294 -4.881938,-15.19104 -4.881938,-15.19104 0,0 3.439718,8.68096 11.392027,8.68096 6.630693,0 10.849311,-5.68597 10.849311,-10.84932 0,-10.319154 -11.816654,-13.844301 -18.444833,-8.680953 z"
|
||||||
|
id="cl-9-8-0" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 121.55789,24.926219 c 0,0 5.96737,-4.773898 5.96737,-11.392027 0,-3.8743954 -3.43971,-10.3065945 -11.39203,-10.3065945 -7.95231,0 -11.39202,6.4347116 -11.39202,10.3065945 0,6.618129 5.96737,11.392027 5.96737,11.392027 -6.62818,-5.163348 -18.444834,-1.638201 -18.444834,8.680956 0,5.16586 4.22113,10.849311 10.849304,10.849311 7.95231,0 11.39203,-8.680956 11.39203,-8.680956 0,0 1.01006,9.894531 -4.88193,15.191045 h 13.02017 c -5.89199,-5.294001 -4.88193,-15.191045 -4.88193,-15.191045 0,0 3.43971,8.680956 11.39202,8.680956 6.63069,0 10.84931,-5.685963 10.84931,-10.849311 0,-10.319157 -11.81665,-13.844304 -18.44483,-8.680956 z"
|
||||||
|
id="cl-9-8-9" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 121.55789,93.027019 c 0,0 5.96737,-4.773898 5.96737,-11.392028 0,-3.874395 -3.43971,-10.306593 -11.39203,-10.306593 -7.95231,0 -11.39202,6.434711 -11.39202,10.306593 0,6.61813 5.96737,11.392028 5.96737,11.392028 -6.62818,-5.163348 -18.444834,-1.638201 -18.444834,8.680951 0,5.16587 4.22113,10.84932 10.849304,10.84932 7.95231,0 11.39203,-8.68096 11.39203,-8.68096 0,0 1.01006,9.89453 -4.88193,15.19104 h 13.02017 c -5.89199,-5.294 -4.88193,-15.19104 -4.88193,-15.19104 0,0 3.43971,8.68096 11.39202,8.68096 6.63069,0 10.84931,-5.68597 10.84931,-10.84932 0,-10.319152 -11.81665,-13.844299 -18.44483,-8.680951 z"
|
||||||
|
id="cl-9-8-0-4" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 89.576798,59.281103 c 0,0 5.967372,-4.773897 5.967372,-11.392027 0,-3.874395 -3.43972,-10.306594 -11.392028,-10.306594 -7.952308,0 -11.392028,6.434712 -11.392028,10.306594 0,6.61813 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163347 -18.444833,-1.638201 -18.444833,8.680957 0,5.165859 4.22113,10.84931 10.849311,10.84931 7.952308,0 11.392027,-8.680956 11.392027,-8.680956 0,0 1.010056,9.894531 -4.881939,15.191045 h 13.020178 c -5.891994,-5.294001 -4.881938,-15.191045 -4.881938,-15.191045 0,0 3.439718,8.680956 11.392027,8.680956 6.63069,0 10.84931,-5.685963 10.84931,-10.84931 0,-10.319158 -11.816653,-13.844304 -18.444832,-8.680957 z"
|
||||||
|
id="cl-9-8-8" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 110.06258,217.80216 c 0,0 -5.96737,4.77391 -5.96737,11.39203 0,3.8744 3.43971,10.3066 11.39202,10.3066 7.95232,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61812 -5.96737,-11.39203 -5.96737,-11.39203 6.62818,5.16335 18.44483,1.6382 18.44483,-8.68095 0,-5.16586 -4.22112,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39202,8.68095 -11.39202,8.68095 0,0 -1.01006,-9.89453 4.88193,-15.19104 h -13.02017 c 5.89199,5.294 4.88193,15.19104 4.88193,15.19104 0,0 -3.43972,-8.68095 -11.39203,-8.68095 -6.630687,0 -10.849305,5.68596 -10.849305,10.84931 0,10.31915 11.816655,13.8443 18.444835,8.68095 z"
|
||||||
|
id="cl-9-8-4" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 110.70832,149.70136 c 0,0 -5.96737,4.77391 -5.96737,11.39203 0,3.8744 3.43971,10.3066 11.39202,10.3066 7.95232,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61812 -5.96737,-11.39203 -5.96737,-11.39203 6.62818,5.16335 18.44483,1.6382 18.44483,-8.68095 0,-5.16586 -4.22112,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39202,8.68095 -11.39202,8.68095 0,0 -1.01006,-9.89453 4.88193,-15.19104 h -13.02017 c 5.89199,5.294 4.88193,15.19104 4.88193,15.19104 0,0 -3.43972,-8.68095 -11.39203,-8.68095 -6.630687,0 -10.849305,5.68596 -10.849305,10.84931 0,10.31915 11.816655,13.8443 18.444835,8.68095 z"
|
||||||
|
id="cl-9-8-0-2" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 46.077633,217.97556 c 0,0 -5.967372,4.77391 -5.967372,11.39203 0,3.8744 3.43972,10.3066 11.392028,10.3066 7.952308,0 11.392028,-6.43471 11.392028,-10.3066 0,-6.61812 -5.967373,-11.39203 -5.967373,-11.39203 6.62818,5.16335 18.444833,1.6382 18.444833,-8.68095 0,-5.16586 -4.22113,-10.84931 -10.849311,-10.84931 -7.952308,0 -11.392027,8.68095 -11.392027,8.68095 0,0 -1.010056,-9.89453 4.881939,-15.19104 H 44.9922 c 5.891994,5.294 4.881938,15.19104 4.881938,15.19104 0,0 -3.439718,-8.68095 -11.392027,-8.68095 -6.630693,0 -10.849311,5.68596 -10.849311,10.84931 0,10.31915 11.816654,13.8443 18.444833,8.68095 z"
|
||||||
|
id="cl-9-8-9-6" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 46.261118,149.87509 c 0,0 -5.967372,4.77391 -5.967372,11.39203 0,3.8744 3.43972,10.3066 11.392028,10.3066 7.952308,0 11.392028,-6.43471 11.392028,-10.3066 0,-6.61812 -5.967373,-11.39203 -5.967373,-11.39203 6.62818,5.16335 18.444833,1.6382 18.444833,-8.68095 0,-5.16586 -4.22113,-10.84931 -10.849311,-10.84931 -7.952308,0 -11.392027,8.68095 -11.392027,8.68095 0,0 -1.010056,-9.89453 4.881939,-15.19104 H 45.175685 c 5.891994,5.294 4.881938,15.19104 4.881938,15.19104 0,0 -3.439718,-8.68095 -11.392027,-8.68095 -6.630693,0 -10.849311,5.68596 -10.849311,10.84931 0,10.31915 11.816654,13.8443 18.444833,8.68095 z"
|
||||||
|
id="cl-9-8-0-4-9" /><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-1.1621548"
|
||||||
|
y="27.170401"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="-1.1621548"
|
||||||
|
y="27.170401"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="11.000458"
|
||||||
|
y="27.499109"
|
||||||
|
id="text3038"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3040"
|
||||||
|
x="11.000458"
|
||||||
|
y="27.499109">0</tspan></text>
|
||||||
|
<path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 78.0698,183.9376 c 0,0 -5.96738,4.77389 -5.96738,11.39202 0,3.8744 3.43972,10.3066 11.39203,10.3066 7.95231,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61813 -5.96737,-11.39202 -5.96737,-11.39202 6.62818,5.16334 18.44483,1.6382 18.44483,-8.68096 0,-5.16586 -4.22113,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39203,8.68096 -11.39203,8.68096 0,0 -1.01005,-9.89454 4.88194,-15.19105 H 76.98436 c 5.892,5.294 4.88194,15.19105 4.88194,15.19105 0,0 -3.43972,-8.68096 -11.39203,-8.68096 -6.630688,0 -10.849308,5.68596 -10.849308,10.84931 0,10.31916 11.816658,13.8443 18.444838,8.68096 z"
|
||||||
|
id="cl-9-8-8-8" /><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-168.80901"
|
||||||
|
y="-216.22618"
|
||||||
|
id="text3788-0"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-6"
|
||||||
|
x="-168.80901"
|
||||||
|
y="-216.22618"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-156.64639"
|
||||||
|
y="-215.89748"
|
||||||
|
id="text3038-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3040-9"
|
||||||
|
x="-156.64639"
|
||||||
|
y="-215.89748">0</tspan></text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 15 KiB |
401
panel/public/cards/10_of_diamonds.svg
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="10_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/10_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="0.4075976"
|
||||||
|
y="26.413288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="0.4075976"
|
||||||
|
y="26.413288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,210.91474)"
|
||||||
|
id="layer1-2-6-8-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,31.619539)"
|
||||||
|
id="layer1-2-6-8-2-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,151.18274)"
|
||||||
|
id="layer1-2-6-8-2-8-1"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,91.351539)"
|
||||||
|
id="layer1-2-6-8-2-8-1-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.89593,210.91474)"
|
||||||
|
id="layer1-2-6-8-8-9"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8-4"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.89593,31.619552)"
|
||||||
|
id="layer1-2-6-8-2-4-9"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3-0"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.89593,151.18274)"
|
||||||
|
id="layer1-2-6-8-2-8-1-9"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4-1"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.89593,91.351542)"
|
||||||
|
id="layer1-2-6-8-2-8-1-4-7"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4-9-7"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,83.213394,61.828949)"
|
||||||
|
id="layer1-2-6-8-2-4-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3-5"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,83.008034,180.83805)"
|
||||||
|
id="layer1-2-6-8-2-4-8-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3-5-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="13.216442"
|
||||||
|
y="26.376137"
|
||||||
|
id="text3788-43"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-1"
|
||||||
|
x="13.216442"
|
||||||
|
y="26.376137"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">0</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-166.43544"
|
||||||
|
y="-215.98416"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-9"
|
||||||
|
x="-166.43544"
|
||||||
|
y="-215.98416"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-153.62659"
|
||||||
|
y="-216.0213"
|
||||||
|
id="text3788-43-2"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-1-0"
|
||||||
|
x="-153.62659"
|
||||||
|
y="-216.0213"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">0</tspan></text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 17 KiB |
407
panel/public/cards/10_of_hearts.svg
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="10_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/10_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="-128.9357"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-0.98704022"
|
||||||
|
y="29.202564"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="-0.98704022"
|
||||||
|
y="29.202564"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,54.512268,30.003768)"
|
||||||
|
id="layer1-9-6-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,54.512268,212.80617)"
|
||||||
|
id="layer1-9-6-8-9"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,54.512268,95.238623)"
|
||||||
|
id="layer1-9-6-8-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,54.512268,145.35601)"
|
||||||
|
id="layer1-9-6-8-884-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-3-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,111.99088,30.602538)"
|
||||||
|
id="layer1-9-6-8-7"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,111.99088,213.40494)"
|
||||||
|
id="layer1-9-6-8-9-7"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5-2"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,111.99088,95.837397)"
|
||||||
|
id="layer1-9-6-8-8-7"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8-2"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,111.99088,145.95478)"
|
||||||
|
id="layer1-9-6-8-884-8-2"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-3-8-6"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,83.213391,62.704546)"
|
||||||
|
id="layer1-9-6-8-91"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-7"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,82.748515,179.70293)"
|
||||||
|
id="layer1-9-6-8-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="12.28669"
|
||||||
|
y="28.960051"
|
||||||
|
id="text3788-43"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-1"
|
||||||
|
x="12.28669"
|
||||||
|
y="28.960051"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">0</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-167.59764"
|
||||||
|
y="-212.05972"
|
||||||
|
id="text3788-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-5"
|
||||||
|
x="-167.59764"
|
||||||
|
y="-212.05972"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-154.32391"
|
||||||
|
y="-212.30225"
|
||||||
|
id="text3788-43-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-1-7"
|
||||||
|
x="-154.32391"
|
||||||
|
y="-212.30225"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">0</tspan></text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 16 KiB |
230
panel/public/cards/10_of_spades.svg
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="10_of_spades.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/10_of_spades.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="106.02254"
|
||||||
|
inkscape:cy="157.08206"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="0.043596052"
|
||||||
|
y="28.342009"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="0.043596052"
|
||||||
|
y="28.342009"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.5085945,0,0,1.3793253,16.929041,45.065897)"
|
||||||
|
id="layer1-7"><path
|
||||||
|
id="sl"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
<g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,53.01089,31.765995)"
|
||||||
|
id="layer1-7-88"><path
|
||||||
|
id="sl-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.5085945,0,0,-1.3793253,150.22511,198.04408)"
|
||||||
|
id="layer1-7-3"><path
|
||||||
|
id="sl-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,53.339713,94.698498)"
|
||||||
|
id="layer1-7-88-8"><path
|
||||||
|
id="sl-4-8"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,53.789261,212.66394)"
|
||||||
|
id="layer1-7-88-4"><path
|
||||||
|
id="sl-4-3"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,53.460438,151.33144)"
|
||||||
|
id="layer1-7-88-8-1"><path
|
||||||
|
id="sl-4-8-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,114.97822,31.578035)"
|
||||||
|
id="layer1-7-88-9"><path
|
||||||
|
id="sl-4-2"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,115.30704,94.510535)"
|
||||||
|
id="layer1-7-88-8-0"><path
|
||||||
|
id="sl-4-8-6"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,115.75659,212.47597)"
|
||||||
|
id="layer1-7-88-4-8"><path
|
||||||
|
id="sl-4-3-9"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,115.42777,151.14347)"
|
||||||
|
id="layer1-7-88-8-1-2"><path
|
||||||
|
id="sl-4-8-4-6"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,84.152135,64.171198)"
|
||||||
|
id="layer1-7-88-6"><path
|
||||||
|
id="sl-4-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,84.480868,180.54025)"
|
||||||
|
id="layer1-7-88-6-6"><path
|
||||||
|
id="sl-4-4-8"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="12.206209"
|
||||||
|
y="28.670717"
|
||||||
|
id="text3038"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3040"
|
||||||
|
x="12.206209"
|
||||||
|
y="28.670717">0</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-166.83667"
|
||||||
|
y="-214.58258"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-166.83667"
|
||||||
|
y="-214.58258"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">1</tspan></text>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-154.67406"
|
||||||
|
y="-214.25388"
|
||||||
|
id="text3038-1"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3040-4"
|
||||||
|
x="-154.67406"
|
||||||
|
y="-214.25388">0</tspan></text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
216
panel/public/cards/2_of_clubs.svg
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="2_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/2_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="117.62976"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
id="text3788-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-7"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-36.788386,-1.5311156)"
|
||||||
|
id="layer1-1-4-8"><path
|
||||||
|
id="cl-9-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,205.12954,245.27515)"
|
||||||
|
id="layer1-1-4-8-0"><path
|
||||||
|
id="cl-9-8-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 8.4 KiB |
318
panel/public/cards/2_of_diamonds.svg
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="2_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/2_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3834-6-0"
|
||||||
|
x="-0.13934441"
|
||||||
|
width="1.2786888"
|
||||||
|
y="-0.16242018"
|
||||||
|
height="1.3248404"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="9.5105772"
|
||||||
|
id="feGaussianBlur3836-6-6" /></filter><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,82.928726,184.02194)"
|
||||||
|
id="layer1-2-6-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,82.928726,55.619539)"
|
||||||
|
id="layer1-2-6-8-2"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
308
panel/public/cards/2_of_hearts.svg
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="2_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/2_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="-28.405554"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,83.701068,56.859768)"
|
||||||
|
id="layer1-9-6-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,83.701068,185.26217)"
|
||||||
|
id="layer1-9-6-8-9"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
147
panel/public/cards/2_of_spades.svg
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="2_of_spades.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/2_of_spades.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="106.02254"
|
||||||
|
inkscape:cy="157.08206"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.5085945,0,0,1.3793253,16.929041,45.065897)"
|
||||||
|
id="layer1-7"><path
|
||||||
|
id="sl"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
<g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,83.41089,57.365995)"
|
||||||
|
id="layer1-7-88"><path
|
||||||
|
id="sl-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
id="text3788-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-6"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">2</tspan></text>
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.5085945,0,0,-1.3793253,150.22511,198.04408)"
|
||||||
|
id="layer1-7-3"><path
|
||||||
|
id="sl-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,83.45613,185.77132)"
|
||||||
|
id="layer1-7-88-7"><path
|
||||||
|
id="sl-4-5"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 5.7 KiB |
224
panel/public/cards/3_of_clubs.svg
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="3_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/3_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="117.62976"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
id="text3788-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-7"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-36.788386,-9.5311159)"
|
||||||
|
id="layer1-1-4-8"><path
|
||||||
|
id="cl-9-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,205.12954,253.27515)"
|
||||||
|
id="layer1-1-4-8-0"><path
|
||||||
|
id="cl-9-8-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-36.788386,60.169684)"
|
||||||
|
id="layer1-1-4-8-2"><path
|
||||||
|
id="cl-9-8-0"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 9.0 KiB |
319
panel/public/cards/3_of_diamonds.svg
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="3_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/3_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,82.928726,192.02194)"
|
||||||
|
id="layer1-2-6-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,82.928726,50.819539)"
|
||||||
|
id="layer1-2-6-8-2"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,82.928726,120.52034)"
|
||||||
|
id="layer1-2-6-8-2-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
318
panel/public/cards/3_of_hearts.svg
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="3_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/3_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="-28.405554"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,83.312268,47.603768)"
|
||||||
|
id="layer1-9-6-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,83.312268,192.00617)"
|
||||||
|
id="layer1-9-6-8-9"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,83.312268,118.90457)"
|
||||||
|
id="layer1-9-6-8-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
154
panel/public/cards/3_of_spades.svg
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="3_of_spades.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/3_of_spades.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="106.02254"
|
||||||
|
inkscape:cy="157.08206"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.5085945,0,0,1.3793253,16.929104,45.065897)"
|
||||||
|
id="layer1-7"><path
|
||||||
|
id="sl"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
<g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,83.41089,49.365995)"
|
||||||
|
id="layer1-7-88"><path
|
||||||
|
id="sl-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
id="text3788-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-6"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">3</tspan></text>
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.5085945,0,0,-1.3793253,150.22511,198.04408)"
|
||||||
|
id="layer1-7-3"><path
|
||||||
|
id="sl-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,83.45613,193.77132)"
|
||||||
|
id="layer1-7-88-7"><path
|
||||||
|
id="sl-4-5"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,83.494697,119.06732)"
|
||||||
|
id="layer1-7-88-6"><path
|
||||||
|
id="sl-4-8"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 6.3 KiB |
230
panel/public/cards/4_of_clubs.svg
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="4_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/4_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="117.62976"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
id="text3788-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-7"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-67.188386,-1.5311156)"
|
||||||
|
id="layer1-1-4-8"><path
|
||||||
|
id="cl-9-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,174.72954,245.27515)"
|
||||||
|
id="layer1-1-4-8-0"><path
|
||||||
|
id="cl-9-8-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-9.1115857,-1.5311131)"
|
||||||
|
id="layer1-1-4-8-2"><path
|
||||||
|
id="cl-9-8-66"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,232.80634,245.27515)"
|
||||||
|
id="layer1-1-4-8-0-4"><path
|
||||||
|
id="cl-9-8-6-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 9.7 KiB |
324
panel/public/cards/4_of_diamonds.svg
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="4_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/4_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.20553,184.02194)"
|
||||||
|
id="layer1-2-6-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.20553,55.619539)"
|
||||||
|
id="layer1-2-6-8-2"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,184.02194)"
|
||||||
|
id="layer1-2-6-8-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,55.619539)"
|
||||||
|
id="layer1-2-6-8-2-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
335
panel/public/cards/4_of_hearts.svg
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="4_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/4_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="-28.405554"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,56.112268,64.859768)"
|
||||||
|
id="layer1-9-6-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,56.112268,177.26217)"
|
||||||
|
id="layer1-9-6-8-9"
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,110.70455,65.1323)"
|
||||||
|
id="layer1-9-6-8-0"
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/layer1-9-6-8-9-8.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-6"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,110.70455,177.53469)"
|
||||||
|
id="layer1-9-6-8-9-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5-9"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
163
panel/public/cards/4_of_spades.svg
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="4_of_spades.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/4_of_spades.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="106.02254"
|
||||||
|
inkscape:cy="157.08206"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.5085945,0,0,1.3793253,16.929041,45.065897)"
|
||||||
|
id="layer1-7"><path
|
||||||
|
id="sl"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
<g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,54.61089,57.365995)"
|
||||||
|
id="layer1-7-88"><path
|
||||||
|
id="sl-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
id="text3788-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-6"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.5085945,0,0,-1.3793253,150.22511,198.04408)"
|
||||||
|
id="layer1-7-3"><path
|
||||||
|
id="sl-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,54.65613,185.77132)"
|
||||||
|
id="layer1-7-88-7"><path
|
||||||
|
id="sl-4-5"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,112.688,57.35436)"
|
||||||
|
id="layer1-7-88-1"><path
|
||||||
|
id="sl-4-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,112.73324,185.75969)"
|
||||||
|
id="layer1-7-88-7-9"><path
|
||||||
|
id="sl-4-5-2"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 6.9 KiB |
238
panel/public/cards/5_of_clubs.svg
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="5_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/5_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="117.62976"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
id="text3788-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-7"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-67.188386,-1.5311156)"
|
||||||
|
id="layer1-1-4-8"><path
|
||||||
|
id="cl-9-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,174.72954,245.27515)"
|
||||||
|
id="layer1-1-4-8-0"><path
|
||||||
|
id="cl-9-8-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-9.1115857,-1.5311131)"
|
||||||
|
id="layer1-1-4-8-2"><path
|
||||||
|
id="cl-9-8-66"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,232.80634,245.27515)"
|
||||||
|
id="layer1-1-4-8-0-4"><path
|
||||||
|
id="cl-9-8-6-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-38.388386,61.769684)"
|
||||||
|
id="layer1-1-4-8-2-6"><path
|
||||||
|
id="cl-9-8-0"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 10 KiB |
333
panel/public/cards/5_of_diamonds.svg
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="5_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/5_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.20553,184.02194)"
|
||||||
|
id="layer1-2-6-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.20553,55.619539)"
|
||||||
|
id="layer1-2-6-8-2"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,184.02194)"
|
||||||
|
id="layer1-2-6-8-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,55.619539)"
|
||||||
|
id="layer1-2-6-8-2-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,82.283636,119.47398)"
|
||||||
|
id="layer1-2-6-8-2-4-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 13 KiB |
336
panel/public/cards/5_of_hearts.svg
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="5_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/5_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="-28.405554"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,56.112268,64.859768)"
|
||||||
|
id="layer1-9-6-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,56.112268,177.26217)"
|
||||||
|
id="layer1-9-6-8-9"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,110.70455,65.1323)"
|
||||||
|
id="layer1-9-6-8-0"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-6"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,110.70455,177.53469)"
|
||||||
|
id="layer1-9-6-8-9-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5-9"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,83.213395,125.05253)"
|
||||||
|
id="layer1-9-6-8-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
170
panel/public/cards/5_of_spades.svg
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="5_of_spades.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/5_of_spades.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="106.02254"
|
||||||
|
inkscape:cy="157.08206"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.5085945,0,0,1.3793253,16.929104,45.065897)"
|
||||||
|
id="layer1-7"><path
|
||||||
|
id="sl"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
<g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,56.21089,49.365995)"
|
||||||
|
id="layer1-7-88"><path
|
||||||
|
id="sl-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
id="text3788-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-6"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">5</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.5085945,0,0,-1.3793253,150.22511,198.04408)"
|
||||||
|
id="layer1-7-3"><path
|
||||||
|
id="sl-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,56.25613,193.77132)"
|
||||||
|
id="layer1-7-88-7"><path
|
||||||
|
id="sl-4-5"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,83.494697,119.06732)"
|
||||||
|
id="layer1-7-88-6"><path
|
||||||
|
id="sl-4-8"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,109.93095,49.495625)"
|
||||||
|
id="layer1-7-88-8"><path
|
||||||
|
id="sl-4-9"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,109.97619,193.90095)"
|
||||||
|
id="layer1-7-88-7-2"><path
|
||||||
|
id="sl-4-5-6"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 7.5 KiB |
244
panel/public/cards/6_of_clubs.svg
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="6_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/6_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="117.62976"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
id="text3788-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-7"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,-9.5311159)"
|
||||||
|
id="layer1-1-4-8"><path
|
||||||
|
id="cl-9-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,177.92954,253.27515)"
|
||||||
|
id="layer1-1-4-8-0"><path
|
||||||
|
id="cl-9-8-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,60.169684)"
|
||||||
|
id="layer1-1-4-8-2"><path
|
||||||
|
id="cl-9-8-0"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,-9.7048439)"
|
||||||
|
id="layer1-1-4-8-8"><path
|
||||||
|
id="cl-9-8-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,230.7146,253.10142)"
|
||||||
|
id="layer1-1-4-8-0-2"><path
|
||||||
|
id="cl-9-8-6-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,59.995956)"
|
||||||
|
id="layer1-1-4-8-2-6"><path
|
||||||
|
id="cl-9-8-0-4"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
340
panel/public/cards/6_of_diamonds.svg
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="6_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/6_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,110.12873,192.02194)"
|
||||||
|
id="layer1-2-6-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,110.12873,50.819539)"
|
||||||
|
id="layer1-2-6-8-2"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,110.12873,120.52034)"
|
||||||
|
id="layer1-2-6-8-2-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,56.013391,192.14005)"
|
||||||
|
id="layer1-2-6-8-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,56.013391,50.937663)"
|
||||||
|
id="layer1-2-6-8-2-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,56.013391,120.63845)"
|
||||||
|
id="layer1-2-6-8-2-8-1"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 14 KiB |
344
panel/public/cards/6_of_hearts.svg
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="6_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/6_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="-28.405554"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,57.712268,47.603768)"
|
||||||
|
id="layer1-9-6-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,57.712268,192.00617)"
|
||||||
|
id="layer1-9-6-8-9"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,57.712268,118.90457)"
|
||||||
|
id="layer1-9-6-8-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,109.0504,47.272778)"
|
||||||
|
id="layer1-9-6-8-88"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-4"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,109.0504,191.67518)"
|
||||||
|
id="layer1-9-6-8-9-3"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,109.0504,118.57358)"
|
||||||
|
id="layer1-9-6-8-8-4"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8-9"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
177
panel/public/cards/6_of_spades.svg
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="6_of_spades.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/6_of_spades.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="106.02254"
|
||||||
|
inkscape:cy="157.08206"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.5085945,0,0,1.3793253,16.929104,45.065897)"
|
||||||
|
id="layer1-7"><path
|
||||||
|
id="sl"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
<g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,56.21089,49.365995)"
|
||||||
|
id="layer1-7-88"><path
|
||||||
|
id="sl-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
id="text3788-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-6"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">6</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.5085945,0,0,-1.3793253,150.22511,198.04408)"
|
||||||
|
id="layer1-7-3"><path
|
||||||
|
id="sl-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,56.25613,193.77132)"
|
||||||
|
id="layer1-7-88-7"><path
|
||||||
|
id="sl-4-5"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,56.294697,119.06732)"
|
||||||
|
id="layer1-7-88-6"><path
|
||||||
|
id="sl-4-8"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,110.89781,49.166905)"
|
||||||
|
id="layer1-7-88-68"><path
|
||||||
|
id="sl-4-84"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,110.94305,193.57223)"
|
||||||
|
id="layer1-7-88-7-3"><path
|
||||||
|
id="sl-4-5-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,110.98162,118.86823)"
|
||||||
|
id="layer1-7-88-6-4"><path
|
||||||
|
id="sl-4-8-9"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 8.1 KiB |
252
panel/public/cards/7_of_clubs.svg
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="7_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/7_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="117.62976"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
id="text3788-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-7"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,-27.131116)"
|
||||||
|
id="layer1-1-4-8"><path
|
||||||
|
id="cl-9-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,177.92954,269.27515)"
|
||||||
|
id="layer1-1-4-8-0"><path
|
||||||
|
id="cl-9-8-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,63.369684)"
|
||||||
|
id="layer1-1-4-8-2"><path
|
||||||
|
id="cl-9-8-0"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,-27.304844)"
|
||||||
|
id="layer1-1-4-8-8"><path
|
||||||
|
id="cl-9-8-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,230.7146,269.10142)"
|
||||||
|
id="layer1-1-4-8-0-2"><path
|
||||||
|
id="cl-9-8-6-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,63.195956)"
|
||||||
|
id="layer1-1-4-8-2-6"><path
|
||||||
|
id="cl-9-8-0-4"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-38.055702,18.622356)"
|
||||||
|
id="layer1-1-4-8-6"><path
|
||||||
|
id="cl-9-8-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
349
panel/public/cards/7_of_diamonds.svg
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="7_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/7_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,111.72873,193.62194)"
|
||||||
|
id="layer1-2-6-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,111.72873,49.219539)"
|
||||||
|
id="layer1-2-6-8-2"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,111.72873,120.52034)"
|
||||||
|
id="layer1-2-6-8-2-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.413391,193.74005)"
|
||||||
|
id="layer1-2-6-8-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.413391,49.337663)"
|
||||||
|
id="layer1-2-6-8-2-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.413391,120.63845)"
|
||||||
|
id="layer1-2-6-8-2-8-1"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,83.213393,85.53779)"
|
||||||
|
id="layer1-2-6-8-2-4-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 14 KiB |
356
panel/public/cards/7_of_hearts.svg
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="7_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/7_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="-28.405554"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,54.512268,31.603768)"
|
||||||
|
id="layer1-9-6-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,54.512268,208.00617)"
|
||||||
|
id="layer1-9-6-8-9"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,54.512268,125.30457)"
|
||||||
|
id="layer1-9-6-8-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,112.2504,31.272778)"
|
||||||
|
id="layer1-9-6-8-88"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-4"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,112.2504,209.27518)"
|
||||||
|
id="layer1-9-6-8-9-3"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,112.2504,124.97358)"
|
||||||
|
id="layer1-9-6-8-8-4"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8-9"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,83.678269,76.705082)"
|
||||||
|
id="layer1-9-6-8-884"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-3"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 13 KiB |
186
panel/public/cards/7_of_spades.svg
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="7_of_spades.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/7_of_spades.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="106.02254"
|
||||||
|
inkscape:cy="157.08206"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.5085945,0,0,1.3793253,16.929104,45.065897)"
|
||||||
|
id="layer1-7"><path
|
||||||
|
id="sl"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
<g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,54.61089,44.565995)"
|
||||||
|
id="layer1-7-88"><path
|
||||||
|
id="sl-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
id="text3788-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-6"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">7</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.5085945,0,0,-1.3793253,150.22511,198.04408)"
|
||||||
|
id="layer1-7-3"><path
|
||||||
|
id="sl-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,54.65613,198.57132)"
|
||||||
|
id="layer1-7-88-7"><path
|
||||||
|
id="sl-4-5"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,54.694697,119.06732)"
|
||||||
|
id="layer1-7-88-6"><path
|
||||||
|
id="sl-4-8"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,112.49781,44.366905)"
|
||||||
|
id="layer1-7-88-68"><path
|
||||||
|
id="sl-4-84"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,112.54305,198.37223)"
|
||||||
|
id="layer1-7-88-7-3"><path
|
||||||
|
id="sl-4-5-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,112.58162,118.86823)"
|
||||||
|
id="layer1-7-88-6-4"><path
|
||||||
|
id="sl-4-8-9"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,83.494697,83.937953)"
|
||||||
|
id="layer1-7-88-688"><path
|
||||||
|
id="sl-4-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 8.6 KiB |
260
panel/public/cards/8_of_clubs.svg
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="8_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/8_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="117.62976"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
id="text3788-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-7"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,-27.131116)"
|
||||||
|
id="layer1-1-4-8"><path
|
||||||
|
id="cl-9-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,177.92954,269.27515)"
|
||||||
|
id="layer1-1-4-8-0"><path
|
||||||
|
id="cl-9-8-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-63.988386,63.369684)"
|
||||||
|
id="layer1-1-4-8-2"><path
|
||||||
|
id="cl-9-8-0"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,-27.304844)"
|
||||||
|
id="layer1-1-4-8-8"><path
|
||||||
|
id="cl-9-8-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,230.7146,269.10142)"
|
||||||
|
id="layer1-1-4-8-0-2"><path
|
||||||
|
id="cl-9-8-6-6"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-11.20333,63.195956)"
|
||||||
|
id="layer1-1-4-8-2-6"><path
|
||||||
|
id="cl-9-8-0-4"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(2.5125778,0,0,2.5125778,-38.055702,18.622356)"
|
||||||
|
id="layer1-1-4-8-6"><path
|
||||||
|
id="cl-9-8-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><g
|
||||||
|
transform="matrix(-2.5125778,0,0,-2.5125778,204.43127,226.5922)"
|
||||||
|
id="layer1-1-4-8-6-8"><path
|
||||||
|
id="cl-9-8-8-8"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
358
panel/public/cards/8_of_diamonds.svg
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="8_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/8_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,111.72873,193.62194)"
|
||||||
|
id="layer1-2-6-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,111.72873,49.219539)"
|
||||||
|
id="layer1-2-6-8-2"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,111.72873,120.52034)"
|
||||||
|
id="layer1-2-6-8-2-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.413391,193.74005)"
|
||||||
|
id="layer1-2-6-8-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.413391,49.337663)"
|
||||||
|
id="layer1-2-6-8-2-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.413391,120.63845)"
|
||||||
|
id="layer1-2-6-8-2-8-1"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,83.213393,85.53779)"
|
||||||
|
id="layer1-2-6-8-2-4-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,83.21339,156.92384)"
|
||||||
|
id="layer1-2-6-8-8-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 15 KiB |
364
panel/public/cards/8_of_hearts.svg
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="8_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/8_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="-28.405554"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.775425"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-158.81761"
|
||||||
|
y="-213.51517"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,54.512268,31.603768)"
|
||||||
|
id="layer1-9-6-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,54.512268,208.00617)"
|
||||||
|
id="layer1-9-6-8-9"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,54.512268,125.30457)"
|
||||||
|
id="layer1-9-6-8-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,112.2504,31.272778)"
|
||||||
|
id="layer1-9-6-8-88"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-4"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,112.2504,209.27518)"
|
||||||
|
id="layer1-9-6-8-9-3"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,112.2504,124.97358)"
|
||||||
|
id="layer1-9-6-8-8-4"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8-9"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,83.678269,76.705082)"
|
||||||
|
id="layer1-9-6-8-884"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-3"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,83.213395,162.70775)"
|
||||||
|
id="layer1-9-6-8-884-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-3-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 14 KiB |
195
panel/public/cards/8_of_spades.svg
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="8_of_spades.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/8_of_spades.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="106.02254"
|
||||||
|
inkscape:cy="157.08206"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.5085945,0,0,1.3793253,16.929104,45.065897)"
|
||||||
|
id="layer1-7"><path
|
||||||
|
id="sl"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
<g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,54.61089,44.565995)"
|
||||||
|
id="layer1-7-88"><path
|
||||||
|
id="sl-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
id="text3788-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-6"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">8</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.5085945,0,0,-1.3793253,150.22511,198.04408)"
|
||||||
|
id="layer1-7-3"><path
|
||||||
|
id="sl-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,54.65613,198.57132)"
|
||||||
|
id="layer1-7-88-7"><path
|
||||||
|
id="sl-4-5"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,54.694697,119.06732)"
|
||||||
|
id="layer1-7-88-6"><path
|
||||||
|
id="sl-4-8"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,112.49781,44.366905)"
|
||||||
|
id="layer1-7-88-68"><path
|
||||||
|
id="sl-4-84"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,112.54305,198.37223)"
|
||||||
|
id="layer1-7-88-7-3"><path
|
||||||
|
id="sl-4-5-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,112.58162,118.86823)"
|
||||||
|
id="layer1-7-88-6-4"><path
|
||||||
|
id="sl-4-8-9"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,83.494697,83.937953)"
|
||||||
|
id="layer1-7-88-688"><path
|
||||||
|
id="sl-4-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,83.165993,161.80325)"
|
||||||
|
id="layer1-7-88-688-6"><path
|
||||||
|
id="sl-4-4-8"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 9.2 KiB |
254
panel/public/cards/9_of_clubs.svg
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="9_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/9_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="117.62976"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.3105459"
|
||||||
|
y="27.548409"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
id="text3788-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-7"
|
||||||
|
x="-158.86395"
|
||||||
|
y="-214.4666"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 57.572834,25.099947 c 0,0 5.967372,-4.773898 5.967372,-11.392027 0,-3.8743954 -3.43972,-10.3065945 -11.392028,-10.3065945 -7.952308,0 -11.392028,6.4347116 -11.392028,10.3065945 0,6.618129 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163348 -18.444833,-1.638201 -18.444833,8.680956 0,5.16586 4.22113,10.849311 10.849311,10.849311 7.952308,0 11.392027,-8.680956 11.392027,-8.680956 0,0 1.010056,9.894531 -4.881939,15.191045 h 13.020178 c -5.891994,-5.294001 -4.881938,-15.191045 -4.881938,-15.191045 0,0 3.439718,8.680956 11.392027,8.680956 6.630693,0 10.849311,-5.685963 10.849311,-10.849311 0,-10.319157 -11.816654,-13.844304 -18.444833,-8.680956 z"
|
||||||
|
id="cl-9-8" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 57.110434,93.200747 c 0,0 5.967372,-4.773898 5.967372,-11.392027 0,-3.874396 -3.43972,-10.306594 -11.392028,-10.306594 -7.952308,0 -11.392028,6.434711 -11.392028,10.306594 0,6.618129 5.967373,11.392027 5.967373,11.392027 -6.62818,-5.163348 -18.444833,-1.638201 -18.444833,8.680953 0,5.16587 4.22113,10.84932 10.849311,10.84932 7.952308,0 11.392027,-8.68096 11.392027,-8.68096 0,0 1.010056,9.89453 -4.881939,15.19104 h 13.020178 c -5.891994,-5.294 -4.881938,-15.19104 -4.881938,-15.19104 0,0 3.439718,8.68096 11.392027,8.68096 6.630693,0 10.849311,-5.68597 10.849311,-10.84932 0,-10.319154 -11.816654,-13.844301 -18.444833,-8.680953 z"
|
||||||
|
id="cl-9-8-0" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 121.55789,24.926219 c 0,0 5.96737,-4.773898 5.96737,-11.392027 0,-3.8743954 -3.43971,-10.3065945 -11.39203,-10.3065945 -7.95231,0 -11.39202,6.4347116 -11.39202,10.3065945 0,6.618129 5.96737,11.392027 5.96737,11.392027 -6.62818,-5.163348 -18.444834,-1.638201 -18.444834,8.680956 0,5.16586 4.22113,10.849311 10.849304,10.849311 7.95231,0 11.39203,-8.680956 11.39203,-8.680956 0,0 1.01006,9.894531 -4.88193,15.191045 h 13.02017 c -5.89199,-5.294001 -4.88193,-15.191045 -4.88193,-15.191045 0,0 3.43971,8.680956 11.39202,8.680956 6.63069,0 10.84931,-5.685963 10.84931,-10.849311 0,-10.319157 -11.81665,-13.844304 -18.44483,-8.680956 z"
|
||||||
|
id="cl-9-8-9" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 121.55789,93.027019 c 0,0 5.96737,-4.773898 5.96737,-11.392028 0,-3.874395 -3.43971,-10.306593 -11.39203,-10.306593 -7.95231,0 -11.39202,6.434711 -11.39202,10.306593 0,6.61813 5.96737,11.392028 5.96737,11.392028 -6.62818,-5.163348 -18.444834,-1.638201 -18.444834,8.680951 0,5.16587 4.22113,10.84932 10.849304,10.84932 7.95231,0 11.39203,-8.68096 11.39203,-8.68096 0,0 1.01006,9.89453 -4.88193,15.19104 h 13.02017 c -5.89199,-5.294 -4.88193,-15.19104 -4.88193,-15.19104 0,0 3.43971,8.68096 11.39202,8.68096 6.63069,0 10.84931,-5.68597 10.84931,-10.84932 0,-10.319152 -11.81665,-13.844299 -18.44483,-8.680951 z"
|
||||||
|
id="cl-9-8-0-4" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 89.576544,59.281103 c 0,0 5.967372,-4.773897 5.967372,-11.392027 0,-3.874395 -3.43972,-10.306594 -11.392028,-10.306594 -7.952308,0 -11.392028,6.434712 -11.392028,10.306594 0,6.61813 5.967373,11.392027 5.967373,11.392027 C 72.099053,54.117756 60.2824,57.642902 60.2824,67.96206 c 0,5.165859 4.22113,10.84931 10.849311,10.84931 7.952308,0 11.392027,-8.680956 11.392027,-8.680956 0,0 1.010056,9.894531 -4.881939,15.191045 h 13.020178 c -5.891994,-5.294001 -4.881938,-15.191045 -4.881938,-15.191045 0,0 3.439718,8.680956 11.392027,8.680956 6.630694,0 10.849314,-5.685963 10.849314,-10.84931 0,-10.319158 -11.816657,-13.844304 -18.444836,-8.680957 z"
|
||||||
|
id="cl-9-8-8" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 110.06258,217.80216 c 0,0 -5.96737,4.77391 -5.96737,11.39203 0,3.8744 3.43971,10.3066 11.39202,10.3066 7.95232,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61812 -5.96737,-11.39203 -5.96737,-11.39203 6.62818,5.16335 18.44483,1.6382 18.44483,-8.68095 0,-5.16586 -4.22112,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39202,8.68095 -11.39202,8.68095 0,0 -1.01006,-9.89453 4.88193,-15.19104 h -13.02017 c 5.89199,5.294 4.88193,15.19104 4.88193,15.19104 0,0 -3.43972,-8.68095 -11.39203,-8.68095 -6.630687,0 -10.849305,5.68596 -10.849305,10.84931 0,10.31915 11.816655,13.8443 18.444835,8.68095 z"
|
||||||
|
id="cl-9-8-4" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 110.70832,149.70136 c 0,0 -5.96737,4.77391 -5.96737,11.39203 0,3.8744 3.43971,10.3066 11.39202,10.3066 7.95232,0 11.39203,-6.43471 11.39203,-10.3066 0,-6.61812 -5.96737,-11.39203 -5.96737,-11.39203 6.62818,5.16335 18.44483,1.6382 18.44483,-8.68095 0,-5.16586 -4.22112,-10.84931 -10.84931,-10.84931 -7.95231,0 -11.39202,8.68095 -11.39202,8.68095 0,0 -1.01006,-9.89453 4.88193,-15.19104 h -13.02017 c 5.89199,5.294 4.88193,15.19104 4.88193,15.19104 0,0 -3.43972,-8.68095 -11.39203,-8.68095 -6.630687,0 -10.849305,5.68596 -10.849305,10.84931 0,10.31915 11.816655,13.8443 18.444835,8.68095 z"
|
||||||
|
id="cl-9-8-0-2" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 46.077528,217.97589 c 0,0 -5.967372,4.77391 -5.967372,11.39203 0,3.8744 3.43972,10.3066 11.392028,10.3066 7.952308,0 11.392028,-6.43471 11.392028,-10.3066 0,-6.61812 -5.967373,-11.39203 -5.967373,-11.39203 6.62818,5.16335 18.444833,1.6382 18.444833,-8.68095 0,-5.16586 -4.22113,-10.84931 -10.849311,-10.84931 -7.952308,0 -11.392027,8.68095 -11.392027,8.68095 0,0 -1.010056,-9.89453 4.881939,-15.19104 H 44.992095 c 5.891994,5.294 4.881938,15.19104 4.881938,15.19104 0,0 -3.439718,-8.68095 -11.392027,-8.68095 -6.630693,0 -10.849311,5.68596 -10.849311,10.84931 0,10.31915 11.816654,13.8443 18.444833,8.68095 z"
|
||||||
|
id="cl-9-8-9-6" /><path
|
||||||
|
style="fill:#000000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 46.261118,149.87509 c 0,0 -5.967372,4.77391 -5.967372,11.39203 0,3.8744 3.43972,10.3066 11.392028,10.3066 7.952308,0 11.392028,-6.43471 11.392028,-10.3066 0,-6.61812 -5.967373,-11.39203 -5.967373,-11.39203 6.62818,5.16335 18.444833,1.6382 18.444833,-8.68095 0,-5.16586 -4.22113,-10.84931 -10.849311,-10.84931 -7.952308,0 -11.392027,8.68095 -11.392027,8.68095 0,0 -1.010056,-9.89453 4.881939,-15.19104 H 45.175685 c 5.891994,5.294 4.881938,15.19104 4.881938,15.19104 0,0 -3.439718,-8.68095 -11.392027,-8.68095 -6.630693,0 -10.849311,5.68596 -10.849311,10.84931 0,10.31915 11.816654,13.8443 18.444833,8.68095 z"
|
||||||
|
id="cl-9-8-0-4-9" /></svg>
|
||||||
|
After Width: | Height: | Size: 14 KiB |
367
panel/public/cards/9_of_diamonds.svg
Normal file
@@ -0,0 +1,367 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="9_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/9_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="7.8456664"
|
||||||
|
y="26.413288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-159.48785"
|
||||||
|
y="-216.71518"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,210.91474)"
|
||||||
|
id="layer1-2-6-8-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,31.619539)"
|
||||||
|
id="layer1-2-6-8-2-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,151.18274)"
|
||||||
|
id="layer1-2-6-8-2-8-1"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,54.128726,91.351539)"
|
||||||
|
id="layer1-2-6-8-2-8-1-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.89593,210.91474)"
|
||||||
|
id="layer1-2-6-8-8-9"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-8-4"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.89593,31.619552)"
|
||||||
|
id="layer1-2-6-8-2-4-9"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3-0"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.89593,151.18274)"
|
||||||
|
id="layer1-2-6-8-2-8-1-9"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4-1"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,112.89593,91.351542)"
|
||||||
|
id="layer1-2-6-8-2-8-1-4-7"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-8-4-9-7"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(2.5882908,0,0,2.5882908,83.213394,61.828949)"
|
||||||
|
id="layer1-2-6-8-2-4-8"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-8-6-3-5"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 15 KiB |
378
panel/public/cards/9_of_hearts.svg
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="9_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/9_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="-0.38353415"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,54.512268,30.003768)"
|
||||||
|
id="layer1-9-6-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,54.512268,212.80617)"
|
||||||
|
id="layer1-9-6-8-9"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,54.512268,95.238623)"
|
||||||
|
id="layer1-9-6-8-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,54.512268,145.35601)"
|
||||||
|
id="layer1-9-6-8-884-8"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-3-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,111.99088,30.602538)"
|
||||||
|
id="layer1-9-6-8-7"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,111.99088,213.40494)"
|
||||||
|
id="layer1-9-6-8-9-7"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-5-2"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,111.99088,95.837397)"
|
||||||
|
id="layer1-9-6-8-8-7"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-8-2"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(-2.7790082,0,0,-2.600887,111.99088,145.95478)"
|
||||||
|
id="layer1-9-6-8-884-8-2"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-3-8-6"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><g
|
||||||
|
transform="matrix(2.7790082,0,0,2.600887,83.213391,62.704546)"
|
||||||
|
id="layer1-9-6-8-91"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-8-7"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.7438745"
|
||||||
|
y="28.013166"
|
||||||
|
id="text3788-43"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-1"
|
||||||
|
x="8.7438745"
|
||||||
|
y="28.013166"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.81783"
|
||||||
|
y="-213.515"
|
||||||
|
id="text3788-43-3"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-1-1"
|
||||||
|
x="-158.81783"
|
||||||
|
y="-213.515"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 14 KiB |
198
panel/public/cards/9_of_spades.svg
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="9_of_spades.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/9_of_spades.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41" /><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="106.02254"
|
||||||
|
inkscape:cy="157.08206"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="8.5467014"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(1.5085945,0,0,1.3793253,16.929041,45.065897)"
|
||||||
|
id="layer1-7"><path
|
||||||
|
id="sl"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g>
|
||||||
|
<g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,53.01089,31.765995)"
|
||||||
|
id="layer1-7-88"><path
|
||||||
|
id="sl-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
id="text3788-7"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-6"
|
||||||
|
x="-158.97775"
|
||||||
|
y="-215.12402"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">9</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.5085945,0,0,-1.3793253,150.22511,198.04408)"
|
||||||
|
id="layer1-7-3"><path
|
||||||
|
id="sl-1"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,53.339713,94.698498)"
|
||||||
|
id="layer1-7-88-8"><path
|
||||||
|
id="sl-4-8"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,53.789261,212.66394)"
|
||||||
|
id="layer1-7-88-4"><path
|
||||||
|
id="sl-4-3"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,53.460438,151.33144)"
|
||||||
|
id="layer1-7-88-8-1"><path
|
||||||
|
id="sl-4-8-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,114.97822,31.578035)"
|
||||||
|
id="layer1-7-88-9"><path
|
||||||
|
id="sl-4-2"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,115.30704,94.510535)"
|
||||||
|
id="layer1-7-88-8-0"><path
|
||||||
|
id="sl-4-8-6"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,115.75659,212.47597)"
|
||||||
|
id="layer1-7-88-4-8"><path
|
||||||
|
id="sl-4-3-9"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(-2.6486789,0,0,-2.4217176,115.42777,151.14347)"
|
||||||
|
id="layer1-7-88-8-1-2"><path
|
||||||
|
id="sl-4-8-4-6"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g><g
|
||||||
|
transform="matrix(2.6486789,0,0,2.4217176,84.152135,64.171198)"
|
||||||
|
id="layer1-7-88-6"><path
|
||||||
|
id="sl-4-4"
|
||||||
|
d="M 7.989,3.103 C 7.747,-0.954 0.242,-8.59 0,-10.5 c -0.242,1.909 -7.747,9.545 -7.989,13.603 -0.169,2.868 1.695,4.057 3.39,4.057 1.8351685,-0.021581 3.3508701,-2.8006944 3.873,-3.341 0.242,0.716 -1.603,6.682 -2.179,6.682 l 5.811,0 C 2.33,10.501 0.485,4.535 0.727,3.819 1.1841472,4.3152961 2.5241276,7.0768295 4.601,7.16 6.295,7.159 8.158,5.971 7.989,3.103 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000"
|
||||||
|
sodipodi:nodetypes="cccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 9.8 KiB |
21
panel/public/cards/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Howard Yeh (https://github.com/hayeah)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
258
panel/public/cards/ace_of_clubs.svg
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="A_of_clubs.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/A_of_clubs.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3760"
|
||||||
|
cx="48.231091"
|
||||||
|
cy="18.137882"
|
||||||
|
fx="48.231091"
|
||||||
|
fy="18.137882"
|
||||||
|
r="9.5"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.5605256,0.01828294,-0.02684055,-2.2909528,123.98377,58.809108)" /><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.65648854;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784"
|
||||||
|
id="radialGradient3792"
|
||||||
|
cx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
fy="511.22299"
|
||||||
|
r="81.902771"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3784"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.53435117;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788" /></linearGradient><filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3834"
|
||||||
|
x="-0.13934441"
|
||||||
|
width="1.2786888"
|
||||||
|
y="-0.16242018"
|
||||||
|
height="1.3248404"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="9.5105772"
|
||||||
|
id="feGaussianBlur3836" /></filter><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3855"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.51908398;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3834-6"
|
||||||
|
x="-0.13934441"
|
||||||
|
width="1.2786888"
|
||||||
|
y="-0.16242018"
|
||||||
|
height="1.3248404"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="9.5105772"
|
||||||
|
id="feGaussianBlur3836-6" /></filter><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="461.84113"
|
||||||
|
fx="181.69392"
|
||||||
|
cy="461.84113"
|
||||||
|
cx="181.69392"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3916"
|
||||||
|
xlink:href="#linearGradient3784-3"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-3"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.70229006;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-86" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-2" /></linearGradient><filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3834-7"
|
||||||
|
x="-0.13934441"
|
||||||
|
width="1.2786888"
|
||||||
|
y="-0.16242018"
|
||||||
|
height="1.3248404"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="9.5105772"
|
||||||
|
id="feGaussianBlur3836-0" /></filter></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="2.4336873"
|
||||||
|
inkscape:cx="188.71531"
|
||||||
|
inkscape:cy="148.16686"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="6.7105455"
|
||||||
|
y="27.548409"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="6.7105455"
|
||||||
|
y="27.548409"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(0.20614599,0,0,0.20614599,8.8705463,16.512759)"
|
||||||
|
id="g3804"><g
|
||||||
|
id="layer1-1"
|
||||||
|
transform="matrix(28.969925,0,0,28.969925,-1031.5368,-187.37665)"><path
|
||||||
|
style="fill:url(#radialGradient3760);fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
id="cl" /></g><path
|
||||||
|
transform="matrix(1.1091261,0,0,1.2071687,-37.349149,-111.34227)"
|
||||||
|
sodipodi:nodetypes="cscsc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3762"
|
||||||
|
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -71.5404,24.83762 -100,48.57143 -27.21033,22.69199 -62.85715,85.71428 -62.85715,85.71428 z"
|
||||||
|
style="fill:url(#radialGradient3792);fill-opacity:1;stroke:none;filter:url(#filter3834)" /><path
|
||||||
|
transform="matrix(1.1091261,0,0,1.2071687,117.2523,-332.26545)"
|
||||||
|
sodipodi:nodetypes="cscsc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3762-6"
|
||||||
|
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -71.5404,24.83762 -100,48.57143 -27.21033,22.69199 -62.85715,85.71428 -62.85715,85.71428 z"
|
||||||
|
style="fill:url(#radialGradient3855);fill-opacity:1;stroke:none;filter:url(#filter3834-6)" /><path
|
||||||
|
transform="matrix(1.1420384,0.7029084,-0.84188482,1.367838,729.37187,-305.07466)"
|
||||||
|
sodipodi:nodetypes="cscsc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3762-7"
|
||||||
|
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -71.5404,24.83762 -100,48.57143 -27.21033,22.69199 -62.85715,85.71428 -62.85715,85.71428 z"
|
||||||
|
style="fill:url(#radialGradient3916);fill-opacity:1;stroke:none;filter:url(#filter3834-7)" /><path
|
||||||
|
id="rect3015"
|
||||||
|
d="m 28.355532,122.02522 0,734.28125 667.156248,0 0,-734.28125 -667.156248,0 z m 334.281258,97.625 c 91.68979,0 131.37499,74.17213 131.37499,118.84375 0,76.30678 -68.8125,131.34375 -68.8125,131.34375 76.42266,-59.5332 212.65625,-18.88573 212.65625,100.09375 0,59.5332 -48.64211,125.09375 -125.09375,125.09375 -91.68982,0 -131.34374,-100.09375 -131.34374,-100.09375 0,0 -11.65322,114.11662 56.28124,175.15625 l -150.12499,0 c 67.93447,-61.0686 56.3125,-175.15625 56.3125,-175.15625 0,0 -39.65394,100.09375 -131.34375,100.09375 -76.42266,0 -125.093758,-65.53158 -125.093758,-125.09375 0,-118.97948 136.233598,-159.62695 212.656258,-100.09375 0,0 -68.8125,-55.03697 -68.8125,-131.34375 0,-44.64265 39.65394,-118.84375 131.34375,-118.84375 z"
|
||||||
|
style="fill:#fffeff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||||
|
inkscape:connector-curvature="0" /></g><g
|
||||||
|
transform="matrix(1.4856506,0,0,1.4856506,-54.024661,10.018072)"
|
||||||
|
id="layer1-1-4"><path
|
||||||
|
id="cl-9"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-160.46396"
|
||||||
|
y="-214.4666"
|
||||||
|
id="text3788-8"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-7"
|
||||||
|
x="-160.46396"
|
||||||
|
y="-214.4666"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.4856506,0,0,-1.4856506,221.19916,232.46182)"
|
||||||
|
id="layer1-1-4-1"><path
|
||||||
|
id="cl-9-7"
|
||||||
|
d="m 50.291466,22.698228 c 0,0 2.375,-1.9 2.375,-4.534 0,-1.542 -1.369,-4.102 -4.534,-4.102 -3.165,0 -4.534,2.561 -4.534,4.102 0,2.634 2.375,4.534 2.375,4.534 -2.638,-2.055 -7.341,-0.652 -7.341,3.455 0,2.056 1.68,4.318 4.318,4.318 3.165,0 4.534,-3.455 4.534,-3.455 0,0 0.402,3.938 -1.943,6.046 h 5.182 c -2.345,-2.107 -1.943,-6.046 -1.943,-6.046 0,0 1.369,3.455 4.534,3.455 2.639,0 4.318,-2.263 4.318,-4.318 0,-4.107 -4.703,-5.51 -7.341,-3.455 z"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:#000000" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
311
panel/public/cards/ace_of_diamonds.svg
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="ace_of_diamonds.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/ace_of_diamonds.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient2984"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop2986" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.64122134;"
|
||||||
|
offset="1"
|
||||||
|
id="stop2988" /></linearGradient><linearGradient
|
||||||
|
id="linearGradient3784-4-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8-2" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-1" /></linearGradient><filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3834-6-0"
|
||||||
|
x="-0.13934441"
|
||||||
|
width="1.2786888"
|
||||||
|
y="-0.16242018"
|
||||||
|
height="1.3248404"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="9.5105772"
|
||||||
|
id="feGaussianBlur3836-6-6" /></filter><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3100"
|
||||||
|
xlink:href="#linearGradient3784-4-4"
|
||||||
|
inkscape:collect="always" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2984"
|
||||||
|
id="radialGradient3137"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
|
||||||
|
cx="1.6632675e-13"
|
||||||
|
cy="-3.2337365"
|
||||||
|
fx="1.6632675e-13"
|
||||||
|
fy="-3.2337365"
|
||||||
|
r="8" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="6.2456665"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="6.2456665"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial;fill:#df0000;fill-opacity:1">A</tspan></text>
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-161.08786"
|
||||||
|
y="-213.51517"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-161.08786"
|
||||||
|
y="-213.51517"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(0.17001436,0,0,0.17001436,19.517107,29.794341)"
|
||||||
|
id="g3011"><g
|
||||||
|
id="layer1-2"
|
||||||
|
transform="matrix(35.005102,0,0,35.005102,369.18369,512.27289)"><path
|
||||||
|
sodipodi:nodetypes="ccccccccc"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
id="dl"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:url(#radialGradient3137);fill-opacity:1" /></g><path
|
||||||
|
transform="matrix(-1.4652123,0.23694327,-0.24538129,-1.5173914,660.30624,1148.701)"
|
||||||
|
sodipodi:nodetypes="cscsc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3762-6"
|
||||||
|
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -71.5404,24.83762 -100,48.57143 -27.21033,22.69199 -62.85715,85.71428 -62.85715,85.71428 z"
|
||||||
|
style="fill:url(#radialGradient3100);fill-opacity:1;stroke:none;filter:url(#filter3834-6-0)" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
|
||||||
|
id="layer1-2-6"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||||
|
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
|
||||||
|
id="layer1-2-6-4"><path
|
||||||
|
style="fill:#df0000"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="dl-6-9"
|
||||||
|
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
|
||||||
|
sodipodi:nodetypes="ccccccccc" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
324
panel/public/cards/ace_of_hearts.svg
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
|
||||||
|
<!-- http://code.google.com/p/vector-playing-cards/ -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="167.0869141pt"
|
||||||
|
height="242.6669922pt"
|
||||||
|
viewBox="0 0 167.0869141 242.6669922"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.0 r9654"
|
||||||
|
sodipodi:docname="A_of_hearts.svg"
|
||||||
|
inkscape:export-filename="/home/byron/art/cards/final/PNGs/A_of_hearts.png"
|
||||||
|
inkscape:export-xdpi="215.44792"
|
||||||
|
inkscape:export-ydpi="215.44792"><metadata
|
||||||
|
id="metadata43"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs41"><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3781"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3773"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3775" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3777" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3773"
|
||||||
|
id="radialGradient3957"
|
||||||
|
cx="-0.15782039"
|
||||||
|
cy="-8.8345356"
|
||||||
|
fx="-0.15782039"
|
||||||
|
fy="-8.8345356"
|
||||||
|
r="7.9997029"
|
||||||
|
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3959"><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3961" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0.64885497;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3963" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
cx="168.02475"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3975"
|
||||||
|
xlink:href="#linearGradient3784-4"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.4351145;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-5"
|
||||||
|
id="radialGradient3929"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-5"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.48854962;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-0" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-3" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3784-4-1"
|
||||||
|
id="radialGradient3927"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
|
||||||
|
cx="168.02475"
|
||||||
|
cy="509.47577"
|
||||||
|
fx="168.02475"
|
||||||
|
fy="509.47577"
|
||||||
|
r="81.902771" /><linearGradient
|
||||||
|
id="linearGradient3784-4-1"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.23664123;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-03" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-6" /></linearGradient><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient3768"
|
||||||
|
id="radialGradient3776"
|
||||||
|
cx="-0.20602037"
|
||||||
|
cy="-4.5786963"
|
||||||
|
fx="-0.20602037"
|
||||||
|
fy="-4.5786963"
|
||||||
|
r="8"
|
||||||
|
gradientTransform="matrix(-1,0,0,-1.7201755,-0.41204074,-13.027194)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
id="linearGradient3768"><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3770" /><stop
|
||||||
|
style="stop-color:#df0000;stop-opacity:0.67175573;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3772" /></linearGradient><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="511.22299"
|
||||||
|
fx="171.48665"
|
||||||
|
cy="511.22299"
|
||||||
|
cx="171.48665"
|
||||||
|
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013"
|
||||||
|
xlink:href="#linearGradient3784-4-6"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-6"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.31297711;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-8" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-8" /></linearGradient><filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3834-6"
|
||||||
|
x="-0.13934441"
|
||||||
|
width="1.2786888"
|
||||||
|
y="-0.16242018"
|
||||||
|
height="1.3248404"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="9.5105772"
|
||||||
|
id="feGaussianBlur3836-6" /></filter><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient4013-8"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /><linearGradient
|
||||||
|
id="linearGradient3784-4-2"><stop
|
||||||
|
style="stop-color:#ffffff;stop-opacity:0.29007635;"
|
||||||
|
offset="0"
|
||||||
|
id="stop3786-8-1" /><stop
|
||||||
|
style="stop-color:#000000;stop-opacity:0;"
|
||||||
|
offset="1"
|
||||||
|
id="stop3788-6-5" /></linearGradient><filter
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3834-6-3"
|
||||||
|
x="-0.13934441"
|
||||||
|
width="1.2786888"
|
||||||
|
y="-0.16242018"
|
||||||
|
height="1.3248404"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="9.5105772"
|
||||||
|
id="feGaussianBlur3836-6-3" /></filter><radialGradient
|
||||||
|
r="81.902771"
|
||||||
|
fy="492.63205"
|
||||||
|
fx="159.35434"
|
||||||
|
cy="492.63205"
|
||||||
|
cx="159.35434"
|
||||||
|
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="radialGradient3073"
|
||||||
|
xlink:href="#linearGradient3784-4-2"
|
||||||
|
inkscape:collect="always" /></defs><sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1680"
|
||||||
|
inkscape:window-height="977"
|
||||||
|
id="namedview39"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.7208768"
|
||||||
|
inkscape:cx="72.124594"
|
||||||
|
inkscape:cy="147.27218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="25"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg2" />
|
||||||
|
<g
|
||||||
|
id="Layer_x0020_1"
|
||||||
|
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
|
||||||
|
<path
|
||||||
|
style="fill:#FFFFFF;stroke-width:0.5;"
|
||||||
|
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
|
||||||
|
id="path5" />
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g7">
|
||||||
|
<g
|
||||||
|
id="g9">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g15">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g19">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g23">
|
||||||
|
<g
|
||||||
|
id="g25">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
style="stroke:none;"
|
||||||
|
id="g31">
|
||||||
|
<g
|
||||||
|
id="g33">
|
||||||
|
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="6.2456665"
|
||||||
|
y="28.013288"
|
||||||
|
id="text3788"
|
||||||
|
sodipodi:linespacing="125%"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790"
|
||||||
|
x="6.2456665"
|
||||||
|
y="28.013288"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial;fill:#df0000;fill-opacity:1">A</tspan></text>
|
||||||
|
|
||||||
|
<g
|
||||||
|
transform="matrix(0.19686979,0,0,0.19686979,11.54991,16.869674)"
|
||||||
|
id="g3036"><g
|
||||||
|
style="stroke:none"
|
||||||
|
id="layer1-9"
|
||||||
|
transform="matrix(34.670635,0,0,32.448413,363.65075,535.3979)"><path
|
||||||
|
sodipodi:nodetypes="scsscss"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
id="hl"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="fill:url(#radialGradient3776);fill-opacity:1;stroke:none" /></g><path
|
||||||
|
transform="matrix(1.484247,0,0,1.537104,-80.688965,-450.59362)"
|
||||||
|
sodipodi:nodetypes="cscsc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3762-6-4"
|
||||||
|
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -71.5404,24.83762 -100,48.57143 -27.21033,22.69199 -62.85715,85.71428 -62.85715,85.71428 z"
|
||||||
|
style="fill:url(#radialGradient4013);fill-opacity:1;stroke:none;filter:url(#filter3834-6)" /><path
|
||||||
|
transform="matrix(1.153293,0.33782551,-0.32928251,1.4016433,422.93775,-451.90481)"
|
||||||
|
sodipodi:nodetypes="cscsc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path3762-6-2-3"
|
||||||
|
d="m 117.3013,604.26609 c 0,0 -8.06755,-94.94997 22.85715,-122.85714 34.76052,-31.36871 140,-11.42857 140,-11.42857 0,0 -63.09295,37.07057 -91.55255,60.80438 -27.21033,22.69199 -71.3046,73.48133 -71.3046,73.48133 z"
|
||||||
|
style="fill:url(#radialGradient3073);fill-opacity:1;stroke:none;filter:url(#filter3834-6-3)" /></g><g
|
||||||
|
transform="matrix(1.6743072,0,0,1.5669921,17.177511,46.385321)"
|
||||||
|
id="layer1-9-6"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g><text
|
||||||
|
xml:space="preserve"
|
||||||
|
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
|
||||||
|
x="-161.08786"
|
||||||
|
y="-213.51517"
|
||||||
|
id="text3788-4"
|
||||||
|
sodipodi:linespacing="125%"
|
||||||
|
transform="scale(-1,-1)"><tspan
|
||||||
|
sodipodi:role="line"
|
||||||
|
id="tspan3790-3"
|
||||||
|
x="-161.08786"
|
||||||
|
y="-213.51517"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">A</tspan></text>
|
||||||
|
<g
|
||||||
|
transform="matrix(-1.6743072,0,0,-1.5669921,150.15601,195.14313)"
|
||||||
|
id="layer1-9-6-5"
|
||||||
|
style="fill:#df0000;fill-opacity:1"><path
|
||||||
|
style="fill:#df0000;fill-opacity:1"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="hl-8-1"
|
||||||
|
d="M 3.676,-9 C 0.433,-9 0,-5.523 0,-5.523 0,-5.523 -0.433,-9 -3.676,-9 -5.946,-9 -8,-7.441 -8,-4.5 -8,-0.614 -1.4208493,3.2938141 0,9 1.35201,3.2985969 8,-0.614 8,-4.5 8,-7.441 5.946,-9 3.676,-9 z"
|
||||||
|
sodipodi:nodetypes="scsscss" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
131
panel/public/cards/ace_of_spades.svg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
panel/public/cards/back.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
224
panel/public/cards/jack_of_clubs.svg
Normal file
|
After Width: | Height: | Size: 450 KiB |
338
panel/public/cards/jack_of_diamonds.svg
Normal file
|
After Width: | Height: | Size: 395 KiB |
330
panel/public/cards/jack_of_hearts.svg
Normal file
|
After Width: | Height: | Size: 616 KiB |
336
panel/public/cards/jack_of_spades.svg
Normal file
|
After Width: | Height: | Size: 686 KiB |
254
panel/public/cards/king_of_clubs.svg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
351
panel/public/cards/king_of_diamonds.svg
Normal file
|
After Width: | Height: | Size: 650 KiB |
337
panel/public/cards/king_of_hearts.svg
Normal file
|
After Width: | Height: | Size: 757 KiB |
329
panel/public/cards/king_of_spades.svg
Normal file
|
After Width: | Height: | Size: 401 KiB |
250
panel/public/cards/queen_of_clubs.svg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
339
panel/public/cards/queen_of_diamonds.svg
Normal file
|
After Width: | Height: | Size: 365 KiB |
331
panel/public/cards/queen_of_hearts.svg
Normal file
|
After Width: | Height: | Size: 608 KiB |
324
panel/public/cards/queen_of_spades.svg
Normal file
|
After Width: | Height: | Size: 426 KiB |
@@ -6,39 +6,17 @@ import Dashboard from "./pages/Dashboard";
|
|||||||
import Settings from "./pages/Settings";
|
import Settings from "./pages/Settings";
|
||||||
import Users from "./pages/Users";
|
import Users from "./pages/Users";
|
||||||
import Items from "./pages/Items";
|
import Items from "./pages/Items";
|
||||||
import PlaceholderPage from "./pages/PlaceholderPage";
|
import Classes from "./pages/Classes";
|
||||||
|
import Quests from "./pages/Quests";
|
||||||
|
import Lootdrops from "./pages/Lootdrops";
|
||||||
|
import Moderation from "./pages/Moderation";
|
||||||
|
import Transactions from "./pages/Transactions";
|
||||||
|
import Leaderboards from "./pages/Leaderboards";
|
||||||
import NotEnrolled from "./pages/NotEnrolled";
|
import NotEnrolled from "./pages/NotEnrolled";
|
||||||
import PlayerDashboard from "./pages/PlayerDashboard";
|
import PlayerDashboard from "./pages/PlayerDashboard";
|
||||||
import { GameLobby } from "./games/GameLobby";
|
import { GameLobby } from "./games/GameLobby";
|
||||||
import { GameRoom } from "./games/GameRoom";
|
import { GameRoom } from "./games/GameRoom";
|
||||||
|
|
||||||
const placeholders: Record<string, { title: string; description: string }> = {
|
|
||||||
classes: {
|
|
||||||
title: "Classes",
|
|
||||||
description: "Manage academy classes, assign Discord roles, and track class balances.",
|
|
||||||
},
|
|
||||||
quests: {
|
|
||||||
title: "Quests",
|
|
||||||
description: "Configure quests with trigger events, targets, and XP/balance rewards.",
|
|
||||||
},
|
|
||||||
lootdrops: {
|
|
||||||
title: "Lootdrops",
|
|
||||||
description: "View active lootdrops, spawn new drops, and manage lootdrop history.",
|
|
||||||
},
|
|
||||||
moderation: {
|
|
||||||
title: "Moderation",
|
|
||||||
description: "Review moderation cases — warnings, timeouts, kicks, bans — and manage appeals.",
|
|
||||||
},
|
|
||||||
transactions: {
|
|
||||||
title: "Transactions",
|
|
||||||
description: "Browse the economy transaction log with filtering by user, type, and date.",
|
|
||||||
},
|
|
||||||
leaderboards: {
|
|
||||||
title: "Leaderboards",
|
|
||||||
description: "View top players by level, wealth, and net worth.",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function AppRoutes() {
|
function AppRoutes() {
|
||||||
const { loading, user, enrolled, logout } = useAuth();
|
const { loading, user, enrolled, logout } = useAuth();
|
||||||
|
|
||||||
@@ -78,7 +56,7 @@ function AppRoutes() {
|
|||||||
|
|
||||||
{/* Player routes */}
|
{/* Player routes */}
|
||||||
<Route path="/dashboard" element={<PlayerDashboard userId={user.discordId} />} />
|
<Route path="/dashboard" element={<PlayerDashboard userId={user.discordId} />} />
|
||||||
<Route path="/leaderboards" element={<PlaceholderPage {...placeholders.leaderboards} />} />
|
<Route path="/leaderboards" element={<Leaderboards />} />
|
||||||
|
|
||||||
{/* Game routes (both roles) */}
|
{/* Game routes (both roles) */}
|
||||||
<Route path="/games" element={<GameLobby />} />
|
<Route path="/games" element={<GameLobby />} />
|
||||||
@@ -90,11 +68,11 @@ function AppRoutes() {
|
|||||||
<Route path="/admin" element={<Dashboard />} />
|
<Route path="/admin" element={<Dashboard />} />
|
||||||
<Route path="/admin/users" element={<Users />} />
|
<Route path="/admin/users" element={<Users />} />
|
||||||
<Route path="/admin/items" element={<Items />} />
|
<Route path="/admin/items" element={<Items />} />
|
||||||
<Route path="/admin/classes" element={<PlaceholderPage {...placeholders.classes} />} />
|
<Route path="/admin/classes" element={<Classes />} />
|
||||||
<Route path="/admin/quests" element={<PlaceholderPage {...placeholders.quests} />} />
|
<Route path="/admin/quests" element={<Quests />} />
|
||||||
<Route path="/admin/lootdrops" element={<PlaceholderPage {...placeholders.lootdrops} />} />
|
<Route path="/admin/lootdrops" element={<Lootdrops />} />
|
||||||
<Route path="/admin/moderation" element={<PlaceholderPage {...placeholders.moderation} />} />
|
<Route path="/admin/moderation" element={<Moderation />} />
|
||||||
<Route path="/admin/transactions" element={<PlaceholderPage {...placeholders.transactions} />} />
|
<Route path="/admin/transactions" element={<Transactions />} />
|
||||||
<Route path="/admin/settings" element={<Settings />} />
|
<Route path="/admin/settings" element={<Settings />} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -27,25 +27,165 @@ interface NavItem {
|
|||||||
icon: React.ComponentType<{ className?: string }>;
|
icon: React.ComponentType<{ className?: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminNavItems: NavItem[] = [
|
interface NavGroup {
|
||||||
{ path: "/admin", label: "Dashboard", icon: LayoutDashboard },
|
label: string | null;
|
||||||
{ path: "/admin/users", label: "Users", icon: Users },
|
items: NavItem[];
|
||||||
{ path: "/admin/items", label: "Items", icon: Package },
|
}
|
||||||
{ path: "/admin/classes", label: "Classes", icon: GraduationCap },
|
|
||||||
{ path: "/admin/quests", label: "Quests", icon: Scroll },
|
const adminNavGroups: NavGroup[] = [
|
||||||
{ path: "/admin/lootdrops", label: "Lootdrops", icon: Gift },
|
{
|
||||||
{ path: "/admin/moderation", label: "Moderation", icon: Shield },
|
label: "Administration",
|
||||||
{ path: "/admin/transactions", label: "Transactions", icon: ArrowLeftRight },
|
items: [
|
||||||
{ path: "/admin/settings", label: "Settings", icon: Settings },
|
{ path: "/admin", label: "Dashboard", icon: LayoutDashboard },
|
||||||
{ path: "/games", label: "Games", icon: Gamepad2 },
|
{ path: "/admin/users", label: "Users", icon: Users },
|
||||||
|
{ path: "/admin/items", label: "Items", icon: Package },
|
||||||
|
{ path: "/admin/classes", label: "Classes", icon: GraduationCap },
|
||||||
|
{ path: "/admin/quests", label: "Quests", icon: Scroll },
|
||||||
|
{ path: "/admin/lootdrops", label: "Lootdrops", icon: Gift },
|
||||||
|
{ path: "/admin/moderation", label: "Moderation", icon: Shield },
|
||||||
|
{ path: "/admin/transactions", label: "Transactions", icon: ArrowLeftRight },
|
||||||
|
{ path: "/admin/settings", label: "Settings", icon: Settings },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Player",
|
||||||
|
items: [
|
||||||
|
{ path: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
|
||||||
|
{ path: "/games", label: "Games", icon: Gamepad2 },
|
||||||
|
{ path: "/leaderboards", label: "Leaderboards", icon: Trophy },
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const playerNavItems: NavItem[] = [
|
const playerNavGroups: NavGroup[] = [
|
||||||
{ path: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
|
{
|
||||||
{ path: "/games", label: "Games", icon: Gamepad2 },
|
label: null,
|
||||||
{ path: "/leaderboards", label: "Leaderboards", icon: Trophy },
|
items: [
|
||||||
|
{ path: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
|
||||||
|
{ path: "/games", label: "Games", icon: Gamepad2 },
|
||||||
|
{ path: "/leaderboards", label: "Leaderboards", icon: Trophy },
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function SidebarNavItem({
|
||||||
|
path,
|
||||||
|
label,
|
||||||
|
icon: Icon,
|
||||||
|
active,
|
||||||
|
showLabel,
|
||||||
|
onNavigate,
|
||||||
|
}: NavItem & { active: boolean; showLabel: boolean; onNavigate: (path: string) => void }) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={() => onNavigate(path)}
|
||||||
|
className={cn(
|
||||||
|
"w-full flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition-colors",
|
||||||
|
active
|
||||||
|
? "bg-primary/15 text-primary border-l-4 border-primary"
|
||||||
|
: "text-text-tertiary hover:bg-primary/8 hover:text-foreground"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Icon className={cn("w-5 h-5 shrink-0", active && "text-primary")} />
|
||||||
|
{showLabel && <span>{label}</span>}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarNavSection({
|
||||||
|
group,
|
||||||
|
index,
|
||||||
|
showLabels,
|
||||||
|
isActive,
|
||||||
|
onNavigate,
|
||||||
|
}: {
|
||||||
|
group: NavGroup;
|
||||||
|
index: number;
|
||||||
|
showLabels: boolean;
|
||||||
|
isActive: (path: string) => boolean;
|
||||||
|
onNavigate: (path: string) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={cn(index > 0 && "mt-4")}>
|
||||||
|
{group.label && showLabels && (
|
||||||
|
<div className="px-3 pb-1.5 pt-1 text-xs font-semibold uppercase tracking-wider text-text-tertiary/60">
|
||||||
|
{group.label}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!showLabels && index > 0 && (
|
||||||
|
<div className="mx-3 mb-2 border-t border-white/10" />
|
||||||
|
)}
|
||||||
|
<div className="space-y-1">
|
||||||
|
{group.items.map((item) => (
|
||||||
|
<SidebarNavItem
|
||||||
|
key={item.path}
|
||||||
|
{...item}
|
||||||
|
active={isActive(item.path)}
|
||||||
|
showLabel={showLabels}
|
||||||
|
onNavigate={onNavigate}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarUserProfile({
|
||||||
|
user,
|
||||||
|
showLabels,
|
||||||
|
collapsed,
|
||||||
|
mobileOpen,
|
||||||
|
logout,
|
||||||
|
onToggleCollapse,
|
||||||
|
}: {
|
||||||
|
user: AuthUser;
|
||||||
|
showLabels: boolean;
|
||||||
|
collapsed: boolean;
|
||||||
|
mobileOpen: boolean;
|
||||||
|
logout: () => Promise<void>;
|
||||||
|
onToggleCollapse: () => void;
|
||||||
|
}) {
|
||||||
|
const avatarUrl = user.avatar
|
||||||
|
? `https://cdn.discordapp.com/avatars/${user.discordId}/${user.avatar}.png?size=64`
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pt-3 p-3 space-y-2">
|
||||||
|
{showLabels && (
|
||||||
|
<div className="flex items-center gap-3 px-2 py-1.5">
|
||||||
|
{avatarUrl ? (
|
||||||
|
<img src={avatarUrl} alt={user.username} className="w-8 h-8 rounded-full" />
|
||||||
|
) : (
|
||||||
|
<div className="w-8 h-8 rounded-full bg-surface flex items-center justify-center text-xs font-medium">
|
||||||
|
{user.username[0]?.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="text-sm font-medium truncate">{user.username}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={cn("flex", collapsed && !mobileOpen ? "flex-col items-center gap-2" : "items-center justify-between px-2")}>
|
||||||
|
<button
|
||||||
|
onClick={logout}
|
||||||
|
className="inline-flex items-center gap-2 rounded-md px-2 py-1.5 text-sm text-text-tertiary hover:text-destructive hover:bg-destructive/10 transition-colors"
|
||||||
|
title="Sign out"
|
||||||
|
>
|
||||||
|
<LogOut className="w-4 h-4" />
|
||||||
|
{showLabels && <span>Sign out</span>}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onToggleCollapse}
|
||||||
|
className="hidden md:block p-1.5 rounded-md text-text-tertiary hover:text-foreground hover:bg-primary/10 transition-colors"
|
||||||
|
title={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||||
|
>
|
||||||
|
{collapsed ? <ChevronRight className="w-4 h-4" /> : <ChevronLeft className="w-4 h-4" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function Layout({
|
export default function Layout({
|
||||||
user,
|
user,
|
||||||
logout,
|
logout,
|
||||||
@@ -60,11 +200,8 @@ export default function Layout({
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const navItems = user.role === "admin" ? adminNavItems : playerNavItems;
|
const navGroups = user.role === "admin" ? adminNavGroups : playerNavGroups;
|
||||||
|
const showLabels = !collapsed || mobileOpen;
|
||||||
const avatarUrl = user.avatar
|
|
||||||
? `https://cdn.discordapp.com/avatars/${user.discordId}/${user.avatar}.png?size=64`
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// Close mobile drawer on route change
|
// Close mobile drawer on route change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -83,65 +220,8 @@ export default function Layout({
|
|||||||
setMobileOpen(false);
|
setMobileOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sidebarContent = (
|
|
||||||
<>
|
|
||||||
<nav className="flex-1 py-3 px-2 space-y-1 overflow-y-auto">
|
|
||||||
{navItems.map(({ path, label, icon: Icon }) => (
|
|
||||||
<button
|
|
||||||
key={path}
|
|
||||||
onClick={() => handleNav(path)}
|
|
||||||
className={cn(
|
|
||||||
"w-full flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition-colors",
|
|
||||||
isActive(path)
|
|
||||||
? "bg-primary/15 text-primary border-l-4 border-primary"
|
|
||||||
: "text-text-tertiary hover:bg-primary/8 hover:text-foreground"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Icon className={cn("w-5 h-5 shrink-0", isActive(path) && "text-primary")} />
|
|
||||||
{(!collapsed || mobileOpen) && <span>{label}</span>}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div className="pt-3 p-3 space-y-2">
|
|
||||||
{(!collapsed || mobileOpen) && (
|
|
||||||
<div className="flex items-center gap-3 px-2 py-1.5">
|
|
||||||
{avatarUrl ? (
|
|
||||||
<img src={avatarUrl} alt={user.username} className="w-8 h-8 rounded-full" />
|
|
||||||
) : (
|
|
||||||
<div className="w-8 h-8 rounded-full bg-surface flex items-center justify-center text-xs font-medium">
|
|
||||||
{user.username[0]?.toUpperCase()}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex-1 min-w-0">
|
|
||||||
<div className="text-sm font-medium truncate">{user.username}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className={cn("flex", collapsed && !mobileOpen ? "flex-col items-center gap-2" : "items-center justify-between px-2")}>
|
|
||||||
<button
|
|
||||||
onClick={logout}
|
|
||||||
className="inline-flex items-center gap-2 rounded-md px-2 py-1.5 text-sm text-text-tertiary hover:text-destructive hover:bg-destructive/10 transition-colors"
|
|
||||||
title="Sign out"
|
|
||||||
>
|
|
||||||
<LogOut className="w-4 h-4" />
|
|
||||||
{(!collapsed || mobileOpen) && <span>Sign out</span>}
|
|
||||||
</button>
|
|
||||||
{/* Collapse toggle only on desktop */}
|
|
||||||
<button
|
|
||||||
onClick={() => setCollapsed((c) => !c)}
|
|
||||||
className="hidden md:block p-1.5 rounded-md text-text-tertiary hover:text-foreground hover:bg-primary/10 transition-colors"
|
|
||||||
title={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
|
||||||
>
|
|
||||||
{collapsed ? <ChevronRight className="w-4 h-4" /> : <ChevronLeft className="w-4 h-4" />}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex">
|
<div className="min-h-screen flex overflow-x-hidden">
|
||||||
{/* Mobile header bar */}
|
{/* Mobile header bar */}
|
||||||
<div className="fixed top-0 left-0 right-0 z-40 flex items-center h-14 px-4 bg-background md:hidden">
|
<div className="fixed top-0 left-0 right-0 z-40 flex items-center h-14 px-4 bg-background md:hidden">
|
||||||
<button
|
<button
|
||||||
@@ -165,10 +245,8 @@ export default function Layout({
|
|||||||
<aside
|
<aside
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-y-0 left-0 z-50 flex flex-col bg-surface-container-low transition-all duration-200",
|
"fixed inset-y-0 left-0 z-50 flex flex-col bg-surface-container-low transition-all duration-200",
|
||||||
// Mobile: off-screen drawer, shown when mobileOpen
|
|
||||||
"w-60 -translate-x-full md:translate-x-0",
|
"w-60 -translate-x-full md:translate-x-0",
|
||||||
mobileOpen && "translate-x-0",
|
mobileOpen && "translate-x-0",
|
||||||
// Desktop: respect collapsed state
|
|
||||||
!mobileOpen && collapsed && "md:w-16"
|
!mobileOpen && collapsed && "md:w-16"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -176,7 +254,6 @@ export default function Layout({
|
|||||||
<div className="font-display text-xl font-bold tracking-tight">
|
<div className="font-display text-xl font-bold tracking-tight">
|
||||||
{collapsed && !mobileOpen ? "A" : "Aurora"}
|
{collapsed && !mobileOpen ? "A" : "Aurora"}
|
||||||
</div>
|
</div>
|
||||||
{/* Close button on mobile */}
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setMobileOpen(false)}
|
onClick={() => setMobileOpen(false)}
|
||||||
className="p-1.5 rounded-md text-text-tertiary hover:text-foreground md:hidden"
|
className="p-1.5 rounded-md text-text-tertiary hover:text-foreground md:hidden"
|
||||||
@@ -185,16 +262,35 @@ export default function Layout({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{sidebarContent}
|
<nav className="flex-1 py-3 px-2 overflow-y-auto">
|
||||||
|
{navGroups.map((group, i) => (
|
||||||
|
<SidebarNavSection
|
||||||
|
key={i}
|
||||||
|
group={group}
|
||||||
|
index={i}
|
||||||
|
showLabels={showLabels}
|
||||||
|
isActive={isActive}
|
||||||
|
onNavigate={handleNav}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<SidebarUserProfile
|
||||||
|
user={user}
|
||||||
|
showLabels={showLabels}
|
||||||
|
collapsed={collapsed}
|
||||||
|
mobileOpen={mobileOpen}
|
||||||
|
logout={logout}
|
||||||
|
onToggleCollapse={() => setCollapsed((c) => !c)}
|
||||||
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main className={cn(
|
<main className={cn(
|
||||||
"flex-1 transition-all duration-200",
|
"min-w-0 flex-1 overflow-x-hidden transition-all duration-200",
|
||||||
// Mobile: no margin, pad for top header bar
|
|
||||||
"mt-14 md:mt-0",
|
"mt-14 md:mt-0",
|
||||||
collapsed ? "md:ml-16" : "md:ml-60"
|
collapsed ? "md:ml-16" : "md:ml-60"
|
||||||
)}>
|
)}>
|
||||||
<div className="max-w-[1600px] mx-auto px-4 py-6 md:px-6 md:py-8">
|
<div className="min-w-0 max-w-[1600px] mx-auto px-4 py-6 md:px-6 md:py-8">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -1,9 +1,26 @@
|
|||||||
import { useState, useEffect } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
ArrowRight,
|
||||||
|
ChevronLeft,
|
||||||
|
Clock3,
|
||||||
|
Coins,
|
||||||
|
Eye,
|
||||||
|
Gamepad2,
|
||||||
|
Sparkles,
|
||||||
|
Swords,
|
||||||
|
Users,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "../lib/utils";
|
||||||
import { useWebSocket } from "../lib/useWebSocket";
|
import { useWebSocket } from "../lib/useWebSocket";
|
||||||
import { gameUIRegistry } from "./registry";
|
import { gameUIRegistry } from "./registry";
|
||||||
|
import {
|
||||||
|
CHESS_TIME_CONTROLS,
|
||||||
|
CHESS_TIME_CONTROL_CATEGORIES,
|
||||||
|
CHESS_TIME_CONTROL_LABELS,
|
||||||
|
} from "./chess/timeControls";
|
||||||
|
|
||||||
// Mirrors RoomSummary in api/src/games/types.ts — keep in sync
|
|
||||||
interface RoomSummary {
|
interface RoomSummary {
|
||||||
id: string;
|
id: string;
|
||||||
gameSlug: string;
|
gameSlug: string;
|
||||||
@@ -13,6 +30,412 @@ interface RoomSummary {
|
|||||||
maxPlayers: number;
|
maxPlayers: number;
|
||||||
spectatorCount: number;
|
spectatorCount: number;
|
||||||
status: "waiting" | "playing" | "finished";
|
status: "waiting" | "playing" | "finished";
|
||||||
|
betAmount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfiguredGame = "chess" | "blackjack" | null;
|
||||||
|
|
||||||
|
const BET_OPTIONS = [0, 10, 25, 50, 100, 250, 500] as const;
|
||||||
|
|
||||||
|
function roomStatusMeta(status: RoomSummary["status"]) {
|
||||||
|
if (status === "waiting") {
|
||||||
|
return {
|
||||||
|
label: "Waiting",
|
||||||
|
chipClassName: "border-warning/25 bg-warning/12 text-warning",
|
||||||
|
accentClassName: "from-warning/18 via-warning/8 to-transparent",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (status === "playing") {
|
||||||
|
return {
|
||||||
|
label: "In Progress",
|
||||||
|
chipClassName: "border-success/25 bg-success/12 text-success",
|
||||||
|
accentClassName: "from-success/18 via-success/8 to-transparent",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label: "Finished",
|
||||||
|
chipClassName: "border-border/70 bg-card/80 text-text-tertiary",
|
||||||
|
accentClassName: "from-white/8 via-transparent to-transparent",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function gameSurfaceClass(slug: string): string {
|
||||||
|
return slug === "blackjack"
|
||||||
|
? "border-emerald-500/20 bg-[radial-gradient(circle_at_top,rgba(34,197,94,0.14),transparent_40%),linear-gradient(180deg,rgba(6,95,70,0.45),rgba(6,78,59,0.12))]"
|
||||||
|
: "border-primary/20 bg-[radial-gradient(circle_at_top,rgba(233,195,73,0.14),transparent_40%),linear-gradient(180deg,rgba(61,46,0,0.35),rgba(61,46,0,0.08))]";
|
||||||
|
}
|
||||||
|
|
||||||
|
function roomActionLabel(room: RoomSummary): string {
|
||||||
|
return room.status === "waiting" ? "Join Room" : "Spectate";
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderRooms(a: RoomSummary, b: RoomSummary): number {
|
||||||
|
const statusWeight = { waiting: 0, playing: 1, finished: 2 } as const;
|
||||||
|
return statusWeight[a.status] - statusWeight[b.status]
|
||||||
|
|| b.playerCount - a.playerCount
|
||||||
|
|| b.spectatorCount - a.spectatorCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetricCard({ label, value, hint }: { label: string; value: string; hint: string }) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-black/10 px-4 py-3 backdrop-blur-sm">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">{label}</div>
|
||||||
|
<div className="mt-2 font-display text-2xl text-foreground">{value}</div>
|
||||||
|
<div className="mt-1 text-xs text-text-tertiary">{hint}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FilterChip({
|
||||||
|
active,
|
||||||
|
children,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
active: boolean;
|
||||||
|
children: ReactNode;
|
||||||
|
onClick: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
className={cn(
|
||||||
|
"rounded-full border px-4 py-2 text-sm font-medium transition-colors",
|
||||||
|
active
|
||||||
|
? "border-primary/35 bg-primary/12 text-primary"
|
||||||
|
: "border-white/10 bg-card/70 text-text-tertiary hover:border-white/20 hover:text-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CreateRoomDialog({
|
||||||
|
configGame,
|
||||||
|
show,
|
||||||
|
onBack,
|
||||||
|
onClose,
|
||||||
|
onChooseGame,
|
||||||
|
onCreateChess,
|
||||||
|
onCreateBlackjack,
|
||||||
|
chessTimeControl,
|
||||||
|
setChessTimeControl,
|
||||||
|
soloMode,
|
||||||
|
setSoloMode,
|
||||||
|
betAmount,
|
||||||
|
setBetAmount,
|
||||||
|
}: {
|
||||||
|
configGame: ConfiguredGame;
|
||||||
|
show: boolean;
|
||||||
|
onBack: () => void;
|
||||||
|
onClose: () => void;
|
||||||
|
onChooseGame: (slug: string) => void;
|
||||||
|
onCreateChess: () => void;
|
||||||
|
onCreateBlackjack: () => void;
|
||||||
|
chessTimeControl: string;
|
||||||
|
setChessTimeControl: (value: string) => void;
|
||||||
|
soloMode: boolean;
|
||||||
|
setSoloMode: (value: boolean | ((previous: boolean) => boolean)) => void;
|
||||||
|
betAmount: number;
|
||||||
|
setBetAmount: (value: number) => void;
|
||||||
|
}) {
|
||||||
|
if (!show) return null;
|
||||||
|
|
||||||
|
const selectedGame = configGame ? gameUIRegistry.get(configGame) : null;
|
||||||
|
const selectedChessControl = CHESS_TIME_CONTROLS.find(control => control.key === chessTimeControl);
|
||||||
|
const selectedChessControlDetail = selectedChessControl?.detail ?? "Balanced default for most quick matches.";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 z-50 flex items-end justify-center bg-black/60 px-3 py-3 sm:items-center sm:px-6"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="max-h-[92vh] w-full max-w-4xl overflow-y-auto rounded-[32px] border border-white/10 bg-surface-container-highest p-5 shadow-[0_32px_90px_rgba(0,0,0,0.52)] sm:p-6"
|
||||||
|
onClick={event => event.stopPropagation()}
|
||||||
|
>
|
||||||
|
{selectedGame ? (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<button
|
||||||
|
onClick={onBack}
|
||||||
|
className="mb-3 inline-flex items-center gap-1 text-xs text-text-tertiary transition-colors hover:text-foreground"
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-3.5 w-3.5" />
|
||||||
|
Back to game selection
|
||||||
|
</button>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<span className="text-3xl">{selectedGame.icon}</span>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<h2 className="font-display text-2xl font-semibold text-foreground">{selectedGame.name}</h2>
|
||||||
|
<p className="mt-1 text-sm text-text-tertiary">{selectedGame.description}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="rounded-full border border-white/10 px-3 py-1.5 text-sm text-text-tertiary transition-colors hover:text-foreground"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{configGame === "chess" ? (
|
||||||
|
<div className="grid gap-6 lg:grid-cols-[minmax(0,1fr)_280px]">
|
||||||
|
<div className="space-y-4">
|
||||||
|
{CHESS_TIME_CONTROL_CATEGORIES.map(category => {
|
||||||
|
const controls = CHESS_TIME_CONTROLS.filter(control => control.category === category);
|
||||||
|
return (
|
||||||
|
<section key={category} className="rounded-3xl border border-white/10 bg-card/70 p-4">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">{category}</div>
|
||||||
|
<div className="mt-3 grid gap-2 sm:grid-cols-2 xl:grid-cols-3">
|
||||||
|
{controls.map(control => {
|
||||||
|
const active = chessTimeControl === control.key;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={control.key}
|
||||||
|
onClick={() => setChessTimeControl(control.key)}
|
||||||
|
className={cn(
|
||||||
|
"rounded-2xl border p-3 text-left transition-colors",
|
||||||
|
active
|
||||||
|
? "border-primary/35 bg-primary/12"
|
||||||
|
: "border-white/10 bg-black/10 hover:border-white/20 hover:bg-black/20",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="text-base font-semibold text-foreground">{control.label}</div>
|
||||||
|
<div className="mt-1 text-xs text-text-tertiary">{control.detail}</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<section className="rounded-3xl border border-white/10 bg-card/70 p-4">
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<div className="text-sm font-semibold text-foreground">Solo Play</div>
|
||||||
|
<div className="mt-1 text-sm text-text-tertiary">
|
||||||
|
Fill both sides with your own session for testing or practice.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setSoloMode(previous => !previous);
|
||||||
|
if (!soloMode) setBetAmount(0);
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"relative mt-0.5 h-7 w-12 rounded-full transition-colors",
|
||||||
|
soloMode ? "bg-primary" : "bg-surface",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"absolute left-1 top-1 h-5 w-5 rounded-full bg-white shadow transition-transform",
|
||||||
|
soloMode && "translate-x-5",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className={cn("rounded-3xl border border-white/10 bg-card/70 p-4", soloMode && "opacity-45")}>
|
||||||
|
<div className="text-sm font-semibold text-foreground">Wager</div>
|
||||||
|
<div className="mt-1 text-sm text-text-tertiary">
|
||||||
|
Stake the match only when both seats are played by different users.
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 flex flex-wrap gap-2">
|
||||||
|
{BET_OPTIONS.map(amount => (
|
||||||
|
<button
|
||||||
|
key={amount}
|
||||||
|
onClick={() => setBetAmount(amount)}
|
||||||
|
disabled={soloMode}
|
||||||
|
className={cn(
|
||||||
|
"rounded-full border px-4 py-2 text-sm font-medium transition-colors",
|
||||||
|
betAmount === amount
|
||||||
|
? amount === 0
|
||||||
|
? "border-white/20 bg-raised text-foreground"
|
||||||
|
: "border-warning/35 bg-warning/12 text-warning"
|
||||||
|
: "border-white/10 bg-black/10 text-text-tertiary hover:text-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{amount === 0 ? "Free" : `${amount} AU`}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside className="space-y-4">
|
||||||
|
<div className={cn("rounded-3xl border p-4", gameSurfaceClass("chess"))}>
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Room Summary</div>
|
||||||
|
<div className="mt-3 text-lg font-semibold text-foreground">{CHESS_TIME_CONTROL_LABELS[chessTimeControl]}</div>
|
||||||
|
<div className="mt-1 text-sm text-text-tertiary">{selectedChessControlDetail}</div>
|
||||||
|
<div className="mt-4 space-y-3 text-sm text-text-secondary">
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<span>Launch</span>
|
||||||
|
<span className="font-medium text-foreground">Auto-start at 2 players</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<span>Mode</span>
|
||||||
|
<span className="font-medium text-foreground">{soloMode ? "Solo practice" : "Head-to-head"}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<span>Stake</span>
|
||||||
|
<span className="font-medium text-foreground">{soloMode || betAmount === 0 ? "Free" : `${betAmount} AU`}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={onCreateChess}
|
||||||
|
className="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-primary px-4 py-3 text-sm font-semibold text-on-primary transition hover:opacity-90"
|
||||||
|
>
|
||||||
|
{soloMode ? "Start Solo Game" : "Create Chess Room"}
|
||||||
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid gap-6 lg:grid-cols-[minmax(0,1fr)_280px]">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<section className={cn("rounded-3xl border p-4", gameSurfaceClass("blackjack"))}>
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Table Format</div>
|
||||||
|
<div className="mt-3 grid gap-3 md:grid-cols-2">
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-black/12 p-4">
|
||||||
|
<div className="text-sm font-semibold text-foreground">Manual Start</div>
|
||||||
|
<div className="mt-1 text-sm text-text-tertiary">
|
||||||
|
The host opens the deal when the table is ready.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-black/12 p-4">
|
||||||
|
<div className="text-sm font-semibold text-foreground">Up To 6 Seats</div>
|
||||||
|
<div className="mt-1 text-sm text-text-tertiary">
|
||||||
|
Spectators can watch and claim an empty seat during betting.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="rounded-3xl border border-white/10 bg-card/70 p-4">
|
||||||
|
<div className="text-sm font-semibold text-foreground">Table Stake</div>
|
||||||
|
<div className="mt-1 text-sm text-text-tertiary">
|
||||||
|
This value becomes the base bet for each new hand at the table.
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 flex flex-wrap gap-2">
|
||||||
|
{BET_OPTIONS.map(amount => (
|
||||||
|
<button
|
||||||
|
key={amount}
|
||||||
|
onClick={() => setBetAmount(amount)}
|
||||||
|
className={cn(
|
||||||
|
"rounded-full border px-4 py-2 text-sm font-medium transition-colors",
|
||||||
|
betAmount === amount
|
||||||
|
? amount === 0
|
||||||
|
? "border-white/20 bg-raised text-foreground"
|
||||||
|
: "border-warning/35 bg-warning/12 text-warning"
|
||||||
|
: "border-white/10 bg-black/10 text-text-tertiary hover:text-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{amount === 0 ? "Free table" : `${amount} AU`}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside className="space-y-4">
|
||||||
|
<div className={cn("rounded-3xl border p-4", gameSurfaceClass("blackjack"))}>
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Room Summary</div>
|
||||||
|
<div className="mt-3 text-lg font-semibold text-foreground">Blackjack Table</div>
|
||||||
|
<div className="mt-1 text-sm text-text-tertiary">
|
||||||
|
Flexible seating with live betting, split hands, and spectator access.
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 space-y-3 text-sm text-text-secondary">
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<span>Launch</span>
|
||||||
|
<span className="font-medium text-foreground">Host starts manually</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<span>Seats</span>
|
||||||
|
<span className="font-medium text-foreground">1 to 6 players</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<span>Stake</span>
|
||||||
|
<span className="font-medium text-foreground">{betAmount === 0 ? "Free" : `${betAmount} AU / hand`}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={onCreateBlackjack}
|
||||||
|
className="inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-primary px-4 py-3 text-sm font-semibold text-on-primary transition hover:opacity-90"
|
||||||
|
>
|
||||||
|
Create Blackjack Table
|
||||||
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="inline-flex items-center gap-2 rounded-full border border-primary/20 bg-primary/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.22em] text-primary">
|
||||||
|
<Sparkles className="h-3.5 w-3.5" />
|
||||||
|
Create Room
|
||||||
|
</div>
|
||||||
|
<h2 className="mt-4 font-display text-3xl font-semibold text-foreground">Choose a game room</h2>
|
||||||
|
<p className="mt-2 max-w-2xl text-sm text-text-tertiary">
|
||||||
|
Pick the table or duel you want to host, then tune the settings before you publish the invite link.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="rounded-full border border-white/10 px-3 py-1.5 text-sm text-text-tertiary transition-colors hover:text-foreground"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
|
{gameUIRegistry.list().map(game => (
|
||||||
|
<button
|
||||||
|
key={game.slug}
|
||||||
|
onClick={() => onChooseGame(game.slug)}
|
||||||
|
className={cn(
|
||||||
|
"group rounded-[28px] border p-5 text-left transition-transform hover:-translate-y-0.5",
|
||||||
|
gameSurfaceClass(game.slug),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<div className="text-4xl">{game.icon}</div>
|
||||||
|
<div className="mt-4 font-display text-2xl font-semibold text-foreground">{game.name}</div>
|
||||||
|
<div className="mt-1 text-sm text-text-secondary">{game.tagline}</div>
|
||||||
|
</div>
|
||||||
|
<ArrowRight className="mt-1 h-5 w-5 text-text-tertiary transition-colors group-hover:text-foreground" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex flex-wrap gap-2 text-xs text-text-tertiary">
|
||||||
|
<span className="rounded-full border border-white/10 bg-black/10 px-3 py-1.5">
|
||||||
|
{game.minPlayers === game.maxPlayers
|
||||||
|
? `${game.maxPlayers} players`
|
||||||
|
: `${game.minPlayers}-${game.maxPlayers} players`}
|
||||||
|
</span>
|
||||||
|
<span className="rounded-full border border-white/10 bg-black/10 px-3 py-1.5">
|
||||||
|
{game.manualStart ? "Manual start" : "Auto start"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GameLobby() {
|
export function GameLobby() {
|
||||||
@@ -21,6 +444,10 @@ export function GameLobby() {
|
|||||||
const [rooms, setRooms] = useState<RoomSummary[]>([]);
|
const [rooms, setRooms] = useState<RoomSummary[]>([]);
|
||||||
const [filter, setFilter] = useState<string | null>(null);
|
const [filter, setFilter] = useState<string | null>(null);
|
||||||
const [showCreate, setShowCreate] = useState(false);
|
const [showCreate, setShowCreate] = useState(false);
|
||||||
|
const [configGame, setConfigGame] = useState<ConfiguredGame>(null);
|
||||||
|
const [chessTimeControl, setChessTimeControl] = useState("blitz_5_3");
|
||||||
|
const [soloMode, setSoloMode] = useState(false);
|
||||||
|
const [betAmount, setBetAmount] = useState(0);
|
||||||
|
|
||||||
const gameTypes = gameUIRegistry.list();
|
const gameTypes = gameUIRegistry.list();
|
||||||
|
|
||||||
@@ -37,129 +464,334 @@ export function GameLobby() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
}, [connected, subscribe, navigate]);
|
}, [connected, navigate, subscribe]);
|
||||||
|
|
||||||
const filteredRooms = filter ? rooms.filter(r => r.gameSlug === filter) : rooms;
|
const activeRooms = useMemo(() => {
|
||||||
const activeRooms = filteredRooms.filter(r => r.status !== "finished");
|
const nextRooms = filter ? rooms.filter(room => room.gameSlug === filter) : rooms;
|
||||||
|
return nextRooms.filter(room => room.status !== "finished").sort(orderRooms);
|
||||||
|
}, [filter, rooms]);
|
||||||
|
|
||||||
function createRoom(gameSlug: string) {
|
const waitingRoomCount = rooms.filter(room => room.status === "waiting").length;
|
||||||
send({ type: "CREATE_ROOM", gameType: gameSlug });
|
const totalPlayers = rooms.reduce((sum, room) => sum + room.playerCount, 0);
|
||||||
|
const totalSpectators = rooms.reduce((sum, room) => sum + room.spectatorCount, 0);
|
||||||
|
const wagerTables = rooms.filter(room => room.betAmount > 0 && room.status !== "finished").length;
|
||||||
|
|
||||||
|
function resetCreateState() {
|
||||||
setShowCreate(false);
|
setShowCreate(false);
|
||||||
|
setConfigGame(null);
|
||||||
|
setChessTimeControl("blitz_5_3");
|
||||||
|
setSoloMode(false);
|
||||||
|
setBetAmount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleGameSelect(gameSlug: string) {
|
||||||
|
if (gameSlug === "chess" || gameSlug === "blackjack") {
|
||||||
|
setConfigGame(gameSlug);
|
||||||
|
setBetAmount(0);
|
||||||
|
if (gameSlug === "chess") setSoloMode(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
send({ type: "CREATE_ROOM", gameType: gameSlug });
|
||||||
|
resetCreateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createChessRoom() {
|
||||||
|
send({
|
||||||
|
type: "CREATE_ROOM",
|
||||||
|
gameType: "chess",
|
||||||
|
options: {
|
||||||
|
timeControl: chessTimeControl,
|
||||||
|
...(soloMode && { soloMode: true }),
|
||||||
|
...(betAmount > 0 && !soloMode && { betAmount }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resetCreateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBlackjackRoom() {
|
||||||
|
send({
|
||||||
|
type: "CREATE_ROOM",
|
||||||
|
gameType: "blackjack",
|
||||||
|
options: {
|
||||||
|
...(betAmount > 0 && { betAmount }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resetCreateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="space-y-6">
|
||||||
<div className="flex items-center justify-between gap-3 mb-4 md:mb-6">
|
<section className="overflow-hidden rounded-[32px] border border-white/10 bg-[radial-gradient(circle_at_top,rgba(233,195,73,0.14),transparent_35%),linear-gradient(180deg,rgba(255,255,255,0.03),rgba(255,255,255,0.01))] p-5 shadow-[0_24px_80px_rgba(0,0,0,0.24)] sm:p-6 lg:p-7">
|
||||||
<div className="min-w-0">
|
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_360px] xl:items-start">
|
||||||
<h1 className="font-display text-lg font-semibold">Games</h1>
|
<div>
|
||||||
<p className="text-sm text-text-tertiary hidden sm:block">Browse and create game rooms</p>
|
<div className="inline-flex items-center gap-2 rounded-full border border-primary/20 bg-primary/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.22em] text-primary">
|
||||||
</div>
|
<Gamepad2 className="h-3.5 w-3.5" />
|
||||||
<button
|
Game Rooms
|
||||||
onClick={() => setShowCreate(true)}
|
</div>
|
||||||
className="rounded-xl bg-primary text-on-primary px-4 py-2 text-sm font-label font-medium hover:opacity-90 transition-colors shrink-0"
|
<h1 className="mt-4 font-display text-4xl font-semibold tracking-tight text-foreground sm:text-5xl">
|
||||||
>
|
Host faster. Join clearer. Spectate without guessing.
|
||||||
+ Create Room
|
</h1>
|
||||||
</button>
|
<p className="mt-3 max-w-2xl text-sm leading-6 text-text-secondary sm:text-base">
|
||||||
</div>
|
Browse open rooms, check seats and stakes at a glance, and create a table with the settings you need.
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="flex gap-2 mb-4 overflow-x-auto pb-1">
|
<div className="mt-5 flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilter(null)}
|
onClick={() => setShowCreate(true)}
|
||||||
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
|
disabled={!connected}
|
||||||
filter === null ? "bg-primary/15 text-primary" : "bg-card text-text-tertiary hover:text-foreground"
|
className="inline-flex items-center justify-center gap-2 rounded-2xl bg-primary px-5 py-3 text-sm font-semibold text-on-primary transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
}`}
|
>
|
||||||
>
|
Create Room
|
||||||
All Games
|
<ArrowRight className="h-4 w-4" />
|
||||||
</button>
|
</button>
|
||||||
{gameTypes.map(g => (
|
<div className={cn(
|
||||||
<button
|
"inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-xs uppercase tracking-[0.18em]",
|
||||||
key={g.slug}
|
connected
|
||||||
onClick={() => setFilter(g.slug)}
|
? "border-success/20 bg-success/10 text-success"
|
||||||
className={`px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
|
: "border-warning/20 bg-warning/10 text-warning",
|
||||||
filter === g.slug ? "bg-primary/15 text-primary" : "bg-card text-text-tertiary hover:text-foreground"
|
)}>
|
||||||
}`}
|
<span className={cn("h-2 w-2 rounded-full", connected ? "bg-success" : "bg-warning")} />
|
||||||
>
|
{connected ? "Live connection" : "Reconnecting"}
|
||||||
{g.icon} {g.name}
|
</div>
|
||||||
</button>
|
</div>
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-card rounded-xl">
|
<div className="mt-6 grid gap-3 md:grid-cols-3">
|
||||||
<div className="flex items-center gap-2 px-5 py-3">
|
<MetricCard label="Active Rooms" value={String(activeRooms.length)} hint="Waiting and live tables." />
|
||||||
<span className="text-sm font-display font-semibold">Active Rooms</span>
|
<MetricCard label="Players" value={String(totalPlayers)} hint="Currently seated across games." />
|
||||||
<span className="text-xs text-text-disabled font-label">({activeRooms.length})</span>
|
<MetricCard label="Spectators" value={String(totalSpectators)} hint="Watching without occupying seats." />
|
||||||
</div>
|
</div>
|
||||||
{activeRooms.length === 0 ? (
|
|
||||||
<div className="px-5 py-8 text-center text-sm text-text-tertiary">
|
|
||||||
No active rooms. Create one to get started!
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<div className="px-2 pb-2 space-y-0.5">
|
<div className="space-y-3">
|
||||||
{activeRooms.map(room => {
|
{gameTypes.map(game => (
|
||||||
const plugin = gameUIRegistry.get(room.gameSlug);
|
<button
|
||||||
return (
|
key={game.slug}
|
||||||
<div key={room.id} className="flex items-center justify-between gap-3 px-3 py-3 md:px-4 hover:bg-raised/30 transition-colors rounded-lg">
|
onClick={() => {
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
setShowCreate(true);
|
||||||
<span className="text-lg shrink-0">{plugin?.icon ?? "🎮"}</span>
|
handleGameSelect(game.slug);
|
||||||
<div className="min-w-0">
|
}}
|
||||||
<div className="text-sm font-medium truncate">{room.gameName}</div>
|
className={cn(
|
||||||
<div className="flex items-center gap-2 text-xs text-text-tertiary">
|
"w-full rounded-[28px] border p-4 text-left transition-transform hover:-translate-y-0.5",
|
||||||
<span className={`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold ${
|
gameSurfaceClass(game.slug),
|
||||||
room.status === "waiting"
|
)}
|
||||||
? "bg-warning/15 text-warning"
|
>
|
||||||
: "bg-success/15 text-success"
|
<div className="flex items-start justify-between gap-4">
|
||||||
}`}>
|
<div>
|
||||||
{room.status === "waiting" ? "Waiting" : "Playing"}
|
<div className="text-3xl">{game.icon}</div>
|
||||||
|
<div className="mt-3 text-lg font-semibold text-foreground">{game.name}</div>
|
||||||
|
<div className="mt-1 text-sm text-text-secondary">{game.tagline}</div>
|
||||||
|
</div>
|
||||||
|
<ArrowRight className="mt-1 h-5 w-5 text-text-tertiary" />
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="rounded-[28px] border border-white/10 bg-card/70 p-4">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Lobby Snapshot</div>
|
||||||
|
<div className="mt-3 grid gap-3 md:grid-cols-3 xl:grid-cols-1">
|
||||||
|
<div className="flex items-center gap-3 text-sm text-text-secondary">
|
||||||
|
<Swords className="h-4 w-4 text-primary" />
|
||||||
|
<span>{waitingRoomCount} room{waitingRoomCount !== 1 ? "s" : ""} waiting to start</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 text-sm text-text-secondary">
|
||||||
|
<Coins className="h-4 w-4 text-warning" />
|
||||||
|
<span>{wagerTables} wager table{wagerTables !== 1 ? "s" : ""} live now</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 text-sm text-text-secondary">
|
||||||
|
<Eye className="h-4 w-4 text-info" />
|
||||||
|
<span>{totalSpectators} spectator{totalSpectators !== 1 ? "s" : ""} across rooms</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_300px]">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<FilterChip active={filter === null} onClick={() => setFilter(null)}>All Games</FilterChip>
|
||||||
|
{gameTypes.map(game => (
|
||||||
|
<FilterChip key={game.slug} active={filter === game.slug} onClick={() => setFilter(game.slug)}>
|
||||||
|
<span className="inline-flex items-center gap-2">
|
||||||
|
<span>{game.icon}</span>
|
||||||
|
{game.name}
|
||||||
|
</span>
|
||||||
|
</FilterChip>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-4 lg:grid-cols-2">
|
||||||
|
{activeRooms.length === 0 ? (
|
||||||
|
<div className="rounded-[28px] border border-dashed border-white/10 bg-card/60 p-8 text-center lg:col-span-2">
|
||||||
|
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-primary">
|
||||||
|
<Sparkles className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 font-display text-2xl font-semibold text-foreground">No active rooms yet</div>
|
||||||
|
<p className="mt-2 text-sm text-text-tertiary">
|
||||||
|
Create a room to get started, or switch filters if you are looking for a different game.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() => setShowCreate(true)}
|
||||||
|
className="mt-5 inline-flex items-center gap-2 rounded-2xl bg-primary px-4 py-2.5 text-sm font-semibold text-on-primary transition hover:opacity-90"
|
||||||
|
>
|
||||||
|
Create the first room
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
activeRooms.map(room => {
|
||||||
|
const plugin = gameUIRegistry.get(room.gameSlug);
|
||||||
|
const status = roomStatusMeta(room.status);
|
||||||
|
const openSeats = Math.max(room.maxPlayers - room.playerCount, 0);
|
||||||
|
const roomFacts = [
|
||||||
|
`${room.playerCount}/${room.maxPlayers} seats filled`,
|
||||||
|
openSeats > 0 ? `${openSeats} open` : "Table full",
|
||||||
|
];
|
||||||
|
|
||||||
|
if (room.betAmount > 0) {
|
||||||
|
roomFacts.push(`${room.betAmount} AU stake`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article
|
||||||
|
key={room.id}
|
||||||
|
className={cn(
|
||||||
|
"group relative overflow-hidden rounded-[30px] border bg-card/70 p-5 shadow-[0_20px_50px_rgba(0,0,0,0.18)]",
|
||||||
|
plugin ? gameSurfaceClass(plugin.slug) : "border-white/10",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={cn("pointer-events-none absolute inset-0 bg-gradient-to-br opacity-60", status.accentClassName)} />
|
||||||
|
<div className="relative flex h-full flex-col">
|
||||||
|
<div className="flex items-start justify-between gap-4">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-3xl">{plugin?.icon ?? "🎮"}</span>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<div className="font-display text-2xl font-semibold text-foreground">{room.gameName}</div>
|
||||||
|
<div className="mt-1 text-sm text-text-secondary">
|
||||||
|
{plugin?.description ?? "Game room"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className={cn("shrink-0 rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em]", status.chipClassName)}>
|
||||||
|
{status.label}
|
||||||
</span>
|
</span>
|
||||||
<span>{room.playerCount}/{room.maxPlayers} players</span>
|
</div>
|
||||||
{room.spectatorCount > 0 && <span>· 👁 {room.spectatorCount}</span>}
|
|
||||||
|
<div className="mt-5 flex flex-wrap gap-2">
|
||||||
|
{roomFacts.map(fact => (
|
||||||
|
<span key={fact} className="rounded-full border border-white/10 bg-black/10 px-3 py-1.5 text-xs text-text-secondary">
|
||||||
|
{fact}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 grid gap-3 md:grid-cols-3">
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-black/10 px-4 py-3">
|
||||||
|
<div className="flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
<Users className="h-3.5 w-3.5" />
|
||||||
|
Players
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-lg font-semibold text-foreground">{room.playerCount}/{room.maxPlayers}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-black/10 px-4 py-3">
|
||||||
|
<div className="flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
<Eye className="h-3.5 w-3.5" />
|
||||||
|
Spectators
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-lg font-semibold text-foreground">{room.spectatorCount}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-black/10 px-4 py-3">
|
||||||
|
<div className="flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
<Clock3 className="h-3.5 w-3.5" />
|
||||||
|
Launch
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-sm font-semibold text-foreground">
|
||||||
|
{plugin?.manualStart ? "Host starts" : "Auto starts"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
|
<div className="break-all text-xs text-text-tertiary">
|
||||||
|
Room ID {room.id.slice(0, 8).toUpperCase()}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => navigate(`/${room.gameSlug}/${room.id}`, {
|
||||||
|
state: { preferAs: room.status === "waiting" ? "player" : "spectator" },
|
||||||
|
})}
|
||||||
|
className={cn(
|
||||||
|
"inline-flex w-full items-center justify-center gap-2 rounded-2xl px-4 py-2.5 text-sm font-semibold transition-colors sm:w-auto",
|
||||||
|
room.status === "waiting"
|
||||||
|
? "bg-primary text-on-primary hover:opacity-90"
|
||||||
|
: "border border-white/10 bg-white/5 text-foreground hover:bg-white/10",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{roomActionLabel(room)}
|
||||||
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
<button
|
);
|
||||||
onClick={() => navigate(`/${room.gameSlug}/${room.id}`, {
|
})
|
||||||
state: { preferAs: room.status === "waiting" ? "player" : "spectator" }
|
)}
|
||||||
})}
|
|
||||||
className={`rounded-xl px-3 py-1.5 text-xs font-label font-semibold transition-colors shrink-0 ${
|
|
||||||
room.status === "waiting"
|
|
||||||
? "bg-primary text-on-primary hover:opacity-90"
|
|
||||||
: "bg-raised text-text-tertiary hover:text-foreground"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{room.status === "waiting" ? "Join" : "Spectate"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showCreate && (
|
|
||||||
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/50" onClick={() => setShowCreate(false)}>
|
|
||||||
<div className="bg-surface-container-highest rounded-xl p-6 w-full sm:max-w-sm shadow-[0_20px_40px_rgba(0,0,0,0.5)]" onClick={e => e.stopPropagation()}>
|
|
||||||
<h2 className="font-display text-base font-semibold mb-4">Create a Room</h2>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{gameTypes.map(g => (
|
|
||||||
<button
|
|
||||||
key={g.slug}
|
|
||||||
onClick={() => createRoom(g.slug)}
|
|
||||||
className="w-full flex items-center gap-3 rounded-xl bg-raised px-4 py-3 text-sm font-medium hover:bg-surface-container-high transition-colors"
|
|
||||||
>
|
|
||||||
<span className="text-lg">{g.icon}</span>
|
|
||||||
<span>{g.name}</span>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => setShowCreate(false)}
|
|
||||||
className="mt-4 w-full text-center text-sm text-text-tertiary hover:text-foreground transition-colors"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
<aside className="space-y-4">
|
||||||
|
<div className="rounded-[28px] border border-white/10 bg-card/70 p-5">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Game Types</div>
|
||||||
|
<div className="mt-4 space-y-3">
|
||||||
|
{gameTypes.map(game => {
|
||||||
|
const roomsForGame = rooms.filter(room => room.gameSlug === game.slug && room.status !== "finished").length;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={game.slug}
|
||||||
|
onClick={() => {
|
||||||
|
setFilter(game.slug);
|
||||||
|
setShowCreate(true);
|
||||||
|
handleGameSelect(game.slug);
|
||||||
|
}}
|
||||||
|
className="w-full rounded-2xl border border-white/10 bg-black/10 p-4 text-left transition-colors hover:border-white/20 hover:bg-black/20"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between gap-3">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<div className="flex items-center gap-3 text-foreground">
|
||||||
|
<span className="text-2xl">{game.icon}</span>
|
||||||
|
<span className="text-base font-semibold">{game.name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-sm text-text-tertiary">{game.tagline}</div>
|
||||||
|
</div>
|
||||||
|
<span className="shrink-0 rounded-full border border-white/10 bg-card px-2.5 py-1 text-xs text-text-secondary">
|
||||||
|
{roomsForGame}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<CreateRoomDialog
|
||||||
|
configGame={configGame}
|
||||||
|
show={showCreate}
|
||||||
|
onBack={() => setConfigGame(null)}
|
||||||
|
onClose={resetCreateState}
|
||||||
|
onChooseGame={handleGameSelect}
|
||||||
|
onCreateChess={createChessRoom}
|
||||||
|
onCreateBlackjack={createBlackjackRoom}
|
||||||
|
chessTimeControl={chessTimeControl}
|
||||||
|
setChessTimeControl={setChessTimeControl}
|
||||||
|
soloMode={soloMode}
|
||||||
|
setSoloMode={setSoloMode}
|
||||||
|
betAmount={betAmount}
|
||||||
|
setBetAmount={setBetAmount}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,168 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useParams, useNavigate, useLocation } from "react-router-dom";
|
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
ArrowLeft,
|
||||||
|
CheckCircle2,
|
||||||
|
Clock3,
|
||||||
|
Coins,
|
||||||
|
Copy,
|
||||||
|
Eye,
|
||||||
|
Loader2,
|
||||||
|
Play,
|
||||||
|
Shield,
|
||||||
|
Sparkles,
|
||||||
|
Users,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { cn } from "../lib/utils";
|
||||||
import { useGameRoom } from "../lib/useGameRoom";
|
import { useGameRoom } from "../lib/useGameRoom";
|
||||||
|
import { CHESS_TIME_CONTROL_LABELS } from "./chess/timeControls";
|
||||||
import { gameUIRegistry } from "./registry";
|
import { gameUIRegistry } from "./registry";
|
||||||
import { Loader2 } from "lucide-react";
|
|
||||||
|
|
||||||
function CopyInviteLink({ url }: { url: string }) {
|
function stateChip(status: "waiting" | "playing" | "finished") {
|
||||||
const [copied, setCopied] = useState(false);
|
if (status === "waiting") return "border-warning/25 bg-warning/12 text-warning";
|
||||||
function copy() {
|
if (status === "playing") return "border-success/25 bg-success/12 text-success";
|
||||||
navigator.clipboard.writeText(url).then(() => {
|
return "border-white/10 bg-card text-text-tertiary";
|
||||||
setCopied(true);
|
}
|
||||||
setTimeout(() => setCopied(false), 2000);
|
|
||||||
});
|
function stateLabel(status: "waiting" | "playing" | "finished") {
|
||||||
}
|
if (status === "waiting") return "Waiting";
|
||||||
|
if (status === "playing") return "Live";
|
||||||
|
return "Finished";
|
||||||
|
}
|
||||||
|
|
||||||
|
function gameSurfaceClass(slug: string): string {
|
||||||
|
return slug === "blackjack"
|
||||||
|
? "border-emerald-500/20 bg-[radial-gradient(circle_at_top,rgba(34,197,94,0.14),transparent_42%),linear-gradient(180deg,rgba(6,95,70,0.34),rgba(6,78,59,0.08))]"
|
||||||
|
: "border-primary/20 bg-[radial-gradient(circle_at_top,rgba(233,195,73,0.14),transparent_42%),linear-gradient(180deg,rgba(61,46,0,0.24),rgba(61,46,0,0.06))]";
|
||||||
|
}
|
||||||
|
|
||||||
|
function MessageState({
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
actionLabel,
|
||||||
|
onAction,
|
||||||
|
loading = false,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
body: string;
|
||||||
|
actionLabel: string;
|
||||||
|
onAction: () => void;
|
||||||
|
loading?: boolean;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center gap-2">
|
<div className="rounded-[32px] border border-white/10 bg-card/70 px-6 py-12 text-center shadow-[0_24px_80px_rgba(0,0,0,0.2)]">
|
||||||
<div className="text-xs text-text-disabled mb-1">Share this link to invite:</div>
|
<div className="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 text-primary">
|
||||||
<div className="flex items-center gap-2 w-full max-w-sm">
|
{loading ? <Loader2 className="h-6 w-6 animate-spin" /> : <Sparkles className="h-6 w-6" />}
|
||||||
<span className="flex-1 font-mono bg-card rounded-lg px-2 py-1.5 text-[11px] text-text-tertiary truncate">
|
</div>
|
||||||
{url}
|
<h1 className="mt-5 font-display text-3xl font-semibold text-foreground">{title}</h1>
|
||||||
</span>
|
<p className="mx-auto mt-2 max-w-xl text-sm text-text-tertiary">{body}</p>
|
||||||
<button
|
<button
|
||||||
onClick={copy}
|
onClick={onAction}
|
||||||
className={`shrink-0 rounded px-3 py-1.5 text-xs font-medium transition-colors ${
|
className="mt-6 inline-flex items-center gap-2 rounded-2xl bg-primary px-4 py-2.5 text-sm font-semibold text-on-primary transition hover:opacity-90"
|
||||||
copied
|
>
|
||||||
? "bg-success/15 text-success"
|
{actionLabel}
|
||||||
: "bg-raised text-text-tertiary hover:text-foreground"
|
</button>
|
||||||
}`}
|
</div>
|
||||||
>
|
);
|
||||||
{copied ? "Copied!" : "Copy"}
|
}
|
||||||
</button>
|
|
||||||
|
function CopyInviteCard({ url }: { url: string }) {
|
||||||
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
async function copy() {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(url);
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false), 1800);
|
||||||
|
} catch {
|
||||||
|
setCopied(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="rounded-[28px] border border-white/10 bg-card/70 p-5">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Invite Link</div>
|
||||||
|
<div className="mt-2 text-sm text-text-secondary">
|
||||||
|
Share this room URL to bring another player directly into the waiting room.
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 rounded-2xl border border-white/10 bg-black/10 p-3">
|
||||||
|
<div className="truncate font-mono text-xs text-text-secondary">{url}</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={copy}
|
||||||
|
className={cn(
|
||||||
|
"mt-4 inline-flex w-full items-center justify-center gap-2 rounded-2xl border px-4 py-3 text-sm font-semibold transition-colors",
|
||||||
|
copied
|
||||||
|
? "border-success/20 bg-success/10 text-success"
|
||||||
|
: "border-white/10 bg-white/5 text-foreground hover:bg-white/10",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{copied ? <CheckCircle2 className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||||
|
{copied ? "Invite Copied" : "Copy Invite Link"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CompactRoomBar({
|
||||||
|
roomCode,
|
||||||
|
state,
|
||||||
|
isSpectator,
|
||||||
|
playerCount,
|
||||||
|
maxPlayers,
|
||||||
|
spectatorCount,
|
||||||
|
facts,
|
||||||
|
onExit,
|
||||||
|
}: {
|
||||||
|
roomCode: string;
|
||||||
|
state: "waiting" | "playing" | "finished";
|
||||||
|
isSpectator: boolean;
|
||||||
|
playerCount: number;
|
||||||
|
maxPlayers: number;
|
||||||
|
spectatorCount: number;
|
||||||
|
facts: Array<{ label: string; value: string }>;
|
||||||
|
onExit: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="sticky top-[4.5rem] z-30 md:top-6">
|
||||||
|
<div className="rounded-[24px] border border-white/10 bg-background/82 p-3 shadow-[0_18px_50px_rgba(0,0,0,0.28)] backdrop-blur-xl">
|
||||||
|
<div className="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between">
|
||||||
|
<div className="flex min-w-0 flex-wrap items-center gap-2">
|
||||||
|
<span className={cn("rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em]", stateChip(state))}>
|
||||||
|
{stateLabel(state)}
|
||||||
|
</span>
|
||||||
|
<span className="rounded-full border border-white/10 bg-black/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-text-secondary">
|
||||||
|
Room {roomCode}
|
||||||
|
</span>
|
||||||
|
{isSpectator && (
|
||||||
|
<span className="rounded-full border border-info/20 bg-info/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-info">
|
||||||
|
Spectating
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={onExit}
|
||||||
|
className="rounded-full border border-white/10 bg-black/10 px-3 py-1.5 text-sm text-text-secondary transition-colors hover:text-foreground sm:self-auto self-start"
|
||||||
|
>
|
||||||
|
Leave
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3 flex flex-wrap gap-2">
|
||||||
|
<span className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-black/10 px-3 py-1.5 text-xs text-text-secondary">
|
||||||
|
<Users className="h-3.5 w-3.5" />
|
||||||
|
{playerCount}/{maxPlayers} players
|
||||||
|
</span>
|
||||||
|
<span className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-black/10 px-3 py-1.5 text-xs text-text-secondary">
|
||||||
|
<Eye className="h-3.5 w-3.5" />
|
||||||
|
{spectatorCount} spectators
|
||||||
|
</span>
|
||||||
|
{facts.map(fact => (
|
||||||
|
<span key={fact.label} className="rounded-full border border-white/10 bg-black/10 px-3 py-1.5 text-xs text-text-secondary">
|
||||||
|
{fact.label}: {fact.value}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -41,152 +175,358 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
|||||||
const preferAs = (location.state as { preferAs?: "player" | "spectator" } | null)?.preferAs ?? "player";
|
const preferAs = (location.state as { preferAs?: "player" | "spectator" } | null)?.preferAs ?? "player";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
gameState, players, spectators, roomStatus,
|
gameState,
|
||||||
isSpectator, gameOver, error, sendAction, leaveRoom, sessionReplaced, rejoin, fillRoom,
|
players,
|
||||||
|
spectators,
|
||||||
|
roomStatus,
|
||||||
|
isSpectator,
|
||||||
|
gameOver,
|
||||||
|
roundResult,
|
||||||
|
error,
|
||||||
|
sendAction,
|
||||||
|
leaveRoom,
|
||||||
|
sessionReplaced,
|
||||||
|
rejoin,
|
||||||
|
fillRoom,
|
||||||
|
startGame,
|
||||||
|
roomOptions,
|
||||||
} = useGameRoom(roomId!, userId, role, preferAs);
|
} = useGameRoom(roomId!, userId, role, preferAs);
|
||||||
|
|
||||||
const plugin = gameSlug ? gameUIRegistry.get(gameSlug) : undefined;
|
const plugin = gameSlug ? gameUIRegistry.get(gameSlug) : undefined;
|
||||||
|
|
||||||
|
function exitRoom() {
|
||||||
|
leaveRoom();
|
||||||
|
navigate("/games");
|
||||||
|
}
|
||||||
|
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
return (
|
return (
|
||||||
<div className="text-center py-16">
|
<MessageState
|
||||||
<div className="text-lg font-display font-semibold mb-2">Unknown Game</div>
|
title="Unknown Game"
|
||||||
<p className="text-sm text-text-tertiary mb-4">The game type "{gameSlug}" doesn't exist.</p>
|
body={`The game type "${gameSlug}" is not registered in the panel.`}
|
||||||
<button onClick={() => navigate("/games")} className="text-sm text-primary hover:underline">
|
actionLabel="Back to Games"
|
||||||
Back to Games
|
onAction={() => navigate("/games")}
|
||||||
</button>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roomStatus === "not_found") {
|
if (roomStatus === "not_found") {
|
||||||
return (
|
return (
|
||||||
<div className="text-center py-16">
|
<MessageState
|
||||||
<div className="text-lg font-display font-semibold mb-2">Room Not Found</div>
|
title="Room Not Found"
|
||||||
<p className="text-sm text-text-tertiary mb-4">This room no longer exists or has expired.</p>
|
body="This room no longer exists, has expired, or was already cleaned up by the server."
|
||||||
<button onClick={() => navigate("/games")} className="text-sm text-primary hover:underline">
|
actionLabel="Back to Games"
|
||||||
Back to Games
|
onAction={() => navigate("/games")}
|
||||||
</button>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roomStatus === "connecting") {
|
if (roomStatus === "connecting") {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center py-16 gap-3">
|
<MessageState
|
||||||
<Loader2 className="w-6 h-6 animate-spin text-text-tertiary" />
|
title={preferAs === "spectator" ? "Joining As Spectator" : "Joining Room"}
|
||||||
<p className="text-sm text-text-tertiary">
|
body="The panel is restoring the room state and syncing your latest seat information."
|
||||||
{preferAs === "spectator" ? "Joining as spectator..." : "Joining room..."}
|
actionLabel="Back to Games"
|
||||||
</p>
|
onAction={() => navigate("/games")}
|
||||||
</div>
|
loading
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const GameComponent = plugin.component;
|
const GameComponent = plugin.component;
|
||||||
|
const roomCode = roomId?.slice(0, 8).toUpperCase() ?? "";
|
||||||
|
const hostPlayer = players[0] ?? null;
|
||||||
|
const isHost = hostPlayer?.discordId === userId;
|
||||||
|
const betAmount = roomOptions.betAmount ?? 0;
|
||||||
|
const timeControl = typeof roomOptions.timeControl === "string"
|
||||||
|
? CHESS_TIME_CONTROL_LABELS[roomOptions.timeControl] ?? roomOptions.timeControl
|
||||||
|
: null;
|
||||||
|
const readyToStart = players.length >= plugin.minPlayers;
|
||||||
|
const startHint = plugin.manualStart
|
||||||
|
? isHost
|
||||||
|
? readyToStart
|
||||||
|
? "You can start the game now."
|
||||||
|
: `Need ${plugin.minPlayers} player${plugin.minPlayers === 1 ? "" : "s"} before you can start.`
|
||||||
|
: `${hostPlayer?.username ?? "The host"} will start the game when the table is ready.`
|
||||||
|
: `This room starts automatically once ${plugin.maxPlayers} seats are filled.`;
|
||||||
|
|
||||||
|
const roomFacts = [
|
||||||
|
{ label: "Format", value: plugin.manualStart ? "Manual start" : "Auto start" },
|
||||||
|
{ label: "Seats", value: plugin.minPlayers === plugin.maxPlayers ? `${plugin.maxPlayers}` : `${plugin.minPlayers}-${plugin.maxPlayers}` },
|
||||||
|
...(timeControl ? [{ label: "Clock", value: timeControl }] : []),
|
||||||
|
...(betAmount > 0 ? [{ label: "Stake", value: `${betAmount} AU` }] : []),
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="space-y-6">
|
||||||
<div className="flex items-center justify-between gap-3 mb-4 md:mb-6">
|
<section className={cn("overflow-hidden rounded-[32px] border p-5 shadow-[0_24px_80px_rgba(0,0,0,0.22)] sm:p-6", gameSurfaceClass(plugin.slug))}>
|
||||||
<div className="flex items-center gap-3 min-w-0">
|
<div className="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between">
|
||||||
<span className="text-xl shrink-0">{plugin.icon}</span>
|
<button
|
||||||
<div className="min-w-0">
|
onClick={exitRoom}
|
||||||
<h1 className="font-display text-base font-semibold truncate">{plugin.name}</h1>
|
className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-black/10 px-3 py-1.5 text-sm text-text-secondary transition-colors hover:text-foreground"
|
||||||
<div className="flex items-center gap-2 text-xs text-text-tertiary">
|
>
|
||||||
<span className={`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold ${
|
<ArrowLeft className="h-4 w-4" />
|
||||||
roomStatus === "waiting" ? "bg-warning/15 text-warning"
|
Back to Games
|
||||||
: roomStatus === "playing" ? "bg-success/15 text-success"
|
</button>
|
||||||
: "bg-card text-text-tertiary"
|
<button
|
||||||
}`}>
|
onClick={exitRoom}
|
||||||
{roomStatus === "waiting" ? "Waiting" : roomStatus === "playing" ? "Playing" : "Finished"}
|
className="rounded-full border border-white/10 bg-black/10 px-3 py-1.5 text-sm text-text-secondary transition-colors hover:text-foreground"
|
||||||
</span>
|
>
|
||||||
{isSpectator && <span className="text-text-disabled">Spectating</span>}
|
Leave Room
|
||||||
<span>👁 {spectators.length}</span>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 grid gap-5 xl:grid-cols-[minmax(0,1fr)_320px] xl:items-start">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<span className="text-4xl">{plugin.icon}</span>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<h1 className="font-display text-3xl font-semibold text-foreground sm:text-4xl">{plugin.name}</h1>
|
||||||
|
<span className={cn("rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em]", stateChip(roomStatus))}>
|
||||||
|
{stateLabel(roomStatus)}
|
||||||
|
</span>
|
||||||
|
{isSpectator && (
|
||||||
|
<span className="rounded-full border border-info/20 bg-info/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-info">
|
||||||
|
Spectating
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="mt-2 max-w-2xl text-sm text-text-secondary">{plugin.description}</p>
|
||||||
|
<div className="mt-4 flex flex-wrap gap-2">
|
||||||
|
<span className="break-all rounded-full border border-white/10 bg-black/10 px-3 py-1.5 text-xs text-text-secondary">
|
||||||
|
Room ID {roomCode}
|
||||||
|
</span>
|
||||||
|
{roomFacts.map(fact => (
|
||||||
|
<span key={fact.label} className="rounded-full border border-white/10 bg-black/10 px-3 py-1.5 text-xs text-text-secondary">
|
||||||
|
{fact.label}: {fact.value}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 grid gap-3 md:grid-cols-3">
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-black/12 px-4 py-3">
|
||||||
|
<div className="flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
<Users className="h-3.5 w-3.5" />
|
||||||
|
Players
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-lg font-semibold text-foreground">{players.length}/{plugin.maxPlayers}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-black/12 px-4 py-3">
|
||||||
|
<div className="flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
<Eye className="h-3.5 w-3.5" />
|
||||||
|
Spectators
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-lg font-semibold text-foreground">{spectators.length}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-black/12 px-4 py-3">
|
||||||
|
<div className="flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
{betAmount > 0 ? <Coins className="h-3.5 w-3.5" /> : <Clock3 className="h-3.5 w-3.5" />}
|
||||||
|
{betAmount > 0 ? "Stake" : "Launch"}
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-sm font-semibold text-foreground">
|
||||||
|
{betAmount > 0 ? `${betAmount} AU${plugin.slug === "blackjack" ? " / hand" : ""}` : plugin.manualStart ? "Host starts" : "Auto starts"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => { leaveRoom(); navigate("/games"); }}
|
|
||||||
className="rounded-md px-3 py-1.5 text-sm font-medium bg-raised text-text-tertiary hover:text-foreground transition-colors shrink-0"
|
|
||||||
>
|
|
||||||
Leave
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{sessionReplaced && (
|
<div className="rounded-[28px] border border-white/10 bg-black/12 p-4">
|
||||||
<div className="mb-4 rounded-xl bg-warning/10 px-4 py-3 flex items-center justify-between gap-3">
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Room Notes</div>
|
||||||
<p className="text-sm text-warning">
|
<div className="mt-3 text-sm text-text-secondary">{startHint}</div>
|
||||||
You opened this game in another tab. Actions from this tab are disabled.
|
{hostPlayer && (
|
||||||
</p>
|
<div className="mt-4 flex items-center justify-between gap-3 rounded-2xl border border-white/10 bg-black/10 px-4 py-3 text-sm">
|
||||||
<button
|
<span className="text-text-tertiary">Host</span>
|
||||||
onClick={rejoin}
|
<span className="min-w-0 truncate text-right font-semibold text-foreground">{hostPlayer.username}</span>
|
||||||
className="shrink-0 text-xs font-medium text-warning underline hover:no-underline"
|
</div>
|
||||||
>
|
)}
|
||||||
Rejoin here
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{error && (
|
|
||||||
<div className="mb-4 rounded-xl bg-destructive/10 px-4 py-2 text-sm text-destructive">
|
|
||||||
{error}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{gameOver && (
|
|
||||||
<div className="mb-4 rounded-xl bg-primary/10 px-4 py-3">
|
|
||||||
<div className="text-sm font-semibold text-primary">
|
|
||||||
{gameOver.winner
|
|
||||||
? `Winner: ${players.find(p => p.discordId === gameOver.winner)?.username ?? gameOver.winner}`
|
|
||||||
: "Draw!"}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-text-tertiary mt-1">Reason: {gameOver.reason}</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{roomStatus === "finished" && (
|
<div className="mt-5 space-y-3">
|
||||||
<div className="mt-4 text-center">
|
{sessionReplaced && (
|
||||||
<button
|
<div className="flex flex-col gap-3 rounded-2xl border border-warning/20 bg-warning/10 px-4 py-3 sm:flex-row sm:flex-wrap sm:items-center sm:justify-between">
|
||||||
onClick={() => { leaveRoom(); navigate("/games"); }}
|
<div className="text-sm text-warning">
|
||||||
className="rounded-xl bg-primary text-on-primary px-5 py-2 text-sm font-label font-medium hover:opacity-90 transition-colors"
|
Another tab claimed this room session. Actions from this tab are currently disabled.
|
||||||
>
|
</div>
|
||||||
Back to Lobby
|
<button
|
||||||
</button>
|
onClick={rejoin}
|
||||||
</div>
|
className="self-start rounded-full border border-warning/20 px-3 py-1.5 text-xs font-semibold uppercase tracking-[0.18em] text-warning transition-colors hover:bg-warning/10"
|
||||||
)}
|
>
|
||||||
|
Rejoin Here
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{roomStatus === "waiting" && (
|
{error && (
|
||||||
<div className="bg-card rounded-xl p-5 md:p-8">
|
<div className="rounded-2xl border border-destructive/20 bg-destructive/10 px-4 py-3 text-sm text-destructive">
|
||||||
<div className="text-sm font-semibold mb-4 text-center">
|
{error}
|
||||||
Waiting for players ({players.length}/{plugin.maxPlayers})
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div className="flex gap-3 justify-center mb-6">
|
|
||||||
{Array.from({ length: plugin.maxPlayers }).map((_, i) => {
|
{gameOver && (
|
||||||
const player = players[i];
|
<div className="rounded-2xl border border-primary/20 bg-primary/10 px-4 py-3">
|
||||||
return (
|
<div className="text-sm font-semibold text-primary">
|
||||||
<div key={i} className={`flex flex-col items-center gap-2 px-4 py-3 rounded-xl ${player ? "bg-primary/10" : "bg-surface"}`}>
|
{gameOver.winner
|
||||||
<div className={`w-10 h-10 rounded-full flex items-center justify-center text-sm font-semibold ${player ? "bg-primary/20 text-primary" : "bg-surface text-text-disabled animate-pulse"}`}>
|
? `Winner: ${players.find(player => player.discordId === gameOver.winner)?.username ?? gameOver.winner}`
|
||||||
{player ? player.username[0]?.toUpperCase() : "?"}
|
: "Draw"}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs font-medium">
|
<div className="mt-1 text-sm text-text-secondary">{gameOver.reason}</div>
|
||||||
{player ? player.username : <span className="text-text-disabled">Waiting...</span>}
|
{gameOver.payout && (
|
||||||
</div>
|
<div className="mt-2 text-sm font-semibold text-warning">
|
||||||
<div className="text-[10px] text-text-disabled">
|
{gameOver.payout.refunded
|
||||||
Player {i + 1}
|
? `Wager refunded: ${gameOver.payout.amount} AU`
|
||||||
</div>
|
: `Payout: ${gameOver.payout.amount} AU`}
|
||||||
</div>
|
</div>
|
||||||
);
|
)}
|
||||||
})}
|
</div>
|
||||||
</div>
|
|
||||||
<CopyInviteLink url={window.location.href} />
|
|
||||||
{role === "admin" && players.length < plugin.maxPlayers && (
|
|
||||||
<button
|
|
||||||
onClick={fillRoom}
|
|
||||||
className="mt-4 w-full max-w-sm rounded-md px-4 py-2 text-sm font-medium bg-warning/10 text-warning border border-warning/30 hover:bg-warning/20 transition-colors"
|
|
||||||
>
|
|
||||||
Start Solo Test
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{roomStatus === "waiting" && (
|
||||||
|
<div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_320px]">
|
||||||
|
<section className="rounded-[32px] border border-white/10 bg-card/70 p-5 shadow-[0_20px_60px_rgba(0,0,0,0.16)] sm:p-6">
|
||||||
|
<div className="flex flex-col gap-4 sm:flex-row sm:flex-wrap sm:items-start sm:justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Waiting Room</div>
|
||||||
|
<h2 className="mt-3 font-display text-3xl font-semibold text-foreground">
|
||||||
|
{plugin.manualStart ? "Seat players and launch when ready" : "Filling seats before the game begins"}
|
||||||
|
</h2>
|
||||||
|
<p className="mt-2 max-w-2xl text-sm text-text-tertiary">
|
||||||
|
{plugin.manualStart
|
||||||
|
? "Hosts can keep collecting players and spectators, then start the table manually."
|
||||||
|
: "The game will start automatically as soon as every required player seat is occupied."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-2xl border border-primary/20 bg-primary/10 px-4 py-3 text-right">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.18em] text-text-disabled">Seats Filled</div>
|
||||||
|
<div className="mt-1 text-2xl font-semibold text-foreground">{players.length}/{plugin.maxPlayers}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6 grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||||
|
{Array.from({ length: plugin.maxPlayers }).map((_, index) => {
|
||||||
|
const player = players[index];
|
||||||
|
const isSeatHost = index === 0 && !!player;
|
||||||
|
const isMe = player?.discordId === userId;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`${player?.discordId ?? "empty"}-${index}`}
|
||||||
|
className={cn(
|
||||||
|
"rounded-[28px] border p-4 transition-colors",
|
||||||
|
player
|
||||||
|
? "border-white/10 bg-black/12"
|
||||||
|
: "border-dashed border-white/10 bg-black/6",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between gap-3">
|
||||||
|
<div className={cn(
|
||||||
|
"flex h-12 w-12 items-center justify-center rounded-2xl text-lg font-semibold",
|
||||||
|
player ? "bg-primary/12 text-primary" : "bg-card text-text-disabled",
|
||||||
|
)}>
|
||||||
|
{player ? player.username[0]?.toUpperCase() : "?"}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap justify-end gap-2">
|
||||||
|
{isSeatHost && (
|
||||||
|
<span className="rounded-full border border-primary/20 bg-primary/10 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-primary">
|
||||||
|
Host
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{isMe && (
|
||||||
|
<span className="rounded-full border border-info/20 bg-info/10 px-2.5 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-info">
|
||||||
|
You
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 text-lg font-semibold text-foreground">
|
||||||
|
{player ? player.username : "Open Seat"}
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 text-sm text-text-tertiary">
|
||||||
|
{player ? `Player ${index + 1}` : "Share the invite to fill this seat."}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<aside className="space-y-4">
|
||||||
|
<CopyInviteCard url={window.location.href} />
|
||||||
|
|
||||||
|
<div className="rounded-[28px] border border-white/10 bg-card/70 p-5">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Room Settings</div>
|
||||||
|
<div className="mt-4 space-y-3">
|
||||||
|
{roomFacts.map(fact => (
|
||||||
|
<div key={fact.label} className="flex flex-col gap-1 rounded-2xl border border-white/10 bg-black/10 px-4 py-3 text-sm sm:flex-row sm:items-center sm:justify-between sm:gap-3">
|
||||||
|
<span className="text-text-tertiary">{fact.label}</span>
|
||||||
|
<span className="min-w-0 font-semibold text-foreground sm:text-right">{fact.value}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-[28px] border border-white/10 bg-card/70 p-5">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Actions</div>
|
||||||
|
<div className="mt-3 text-sm text-text-secondary">{startHint}</div>
|
||||||
|
|
||||||
|
{plugin.manualStart && isHost && (
|
||||||
|
<button
|
||||||
|
onClick={startGame}
|
||||||
|
disabled={!readyToStart}
|
||||||
|
className="mt-4 inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-primary px-4 py-3 text-sm font-semibold text-on-primary transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-45"
|
||||||
|
>
|
||||||
|
<Play className="h-4 w-4" />
|
||||||
|
Start Game
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{role === "admin" && players.length < plugin.maxPlayers && (
|
||||||
|
<button
|
||||||
|
onClick={fillRoom}
|
||||||
|
className="mt-3 inline-flex w-full items-center justify-center gap-2 rounded-2xl border border-warning/20 bg-warning/10 px-4 py-3 text-sm font-semibold text-warning transition hover:bg-warning/15"
|
||||||
|
>
|
||||||
|
<Shield className="h-4 w-4" />
|
||||||
|
Fill Room For Test
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-[28px] border border-white/10 bg-card/70 p-5">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-text-disabled">Spectators</div>
|
||||||
|
{spectators.length === 0 ? (
|
||||||
|
<div className="mt-3 rounded-2xl border border-dashed border-white/10 bg-black/8 px-4 py-5 text-sm text-text-tertiary">
|
||||||
|
No spectators yet.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-4 space-y-3">
|
||||||
|
{spectators.map(spectator => (
|
||||||
|
<div key={spectator.discordId} className="flex items-center justify-between gap-3 rounded-2xl border border-white/10 bg-black/10 px-4 py-3 text-sm">
|
||||||
|
<span className="min-w-0 truncate font-medium text-foreground">{spectator.username}</span>
|
||||||
|
<span className="text-text-tertiary">Watching</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(roomStatus === "playing" || roomStatus === "finished") && (
|
||||||
|
<CompactRoomBar
|
||||||
|
roomCode={roomCode}
|
||||||
|
state={roomStatus}
|
||||||
|
isSpectator={isSpectator}
|
||||||
|
playerCount={players.length}
|
||||||
|
maxPlayers={plugin.maxPlayers}
|
||||||
|
spectatorCount={spectators.length}
|
||||||
|
facts={roomFacts}
|
||||||
|
onExit={exitRoom}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(roomStatus === "playing" || roomStatus === "finished") && gameState != null && (
|
{(roomStatus === "playing" || roomStatus === "finished") && gameState != null && (
|
||||||
@@ -196,8 +536,21 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
|||||||
isSpectator={isSpectator}
|
isSpectator={isSpectator}
|
||||||
onAction={sendAction}
|
onAction={sendAction}
|
||||||
players={players}
|
players={players}
|
||||||
|
roundResult={roundResult}
|
||||||
|
roomOptions={roomOptions}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{roomStatus === "finished" && (
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<button
|
||||||
|
onClick={exitRoom}
|
||||||
|
className="inline-flex items-center gap-2 rounded-2xl bg-primary px-4 py-2.5 text-sm font-semibold text-on-primary transition hover:opacity-90"
|
||||||
|
>
|
||||||
|
Back to Lobby
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
905
panel/src/games/blackjack/BlackjackGame.tsx
Normal file
@@ -0,0 +1,905 @@
|
|||||||
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import type { CSSProperties, ReactNode } from "react";
|
||||||
|
import {
|
||||||
|
AlertCircle,
|
||||||
|
ArrowLeftRight,
|
||||||
|
Check,
|
||||||
|
ChevronsUp,
|
||||||
|
Coins,
|
||||||
|
Eye,
|
||||||
|
Hand,
|
||||||
|
type LucideIcon,
|
||||||
|
LogOut,
|
||||||
|
Plus,
|
||||||
|
Square,
|
||||||
|
Trophy,
|
||||||
|
} from "lucide-react";
|
||||||
|
import type { GameUIProps } from "../registry";
|
||||||
|
|
||||||
|
interface Card {
|
||||||
|
suit: "hearts" | "diamonds" | "clubs" | "spades";
|
||||||
|
rank: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlayerHandView {
|
||||||
|
cards: Card[];
|
||||||
|
value: number;
|
||||||
|
status: "playing" | "stood" | "bust" | "blackjack";
|
||||||
|
result: "win" | "blackjack" | "push" | "lose" | null;
|
||||||
|
resultReason: string | null;
|
||||||
|
bet: number;
|
||||||
|
wager: number;
|
||||||
|
payout: number | null;
|
||||||
|
net: number | null;
|
||||||
|
fromSplit: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlayerSeatView {
|
||||||
|
hands: PlayerHandView[];
|
||||||
|
activeHandIndex: number;
|
||||||
|
hasBet: boolean;
|
||||||
|
totalWager: number;
|
||||||
|
roundNet: number | null;
|
||||||
|
cumulativePnl: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BlackjackViewBase {
|
||||||
|
dealerHand: Card[];
|
||||||
|
dealerVisibleValue: number;
|
||||||
|
dealerFullValue: number | null;
|
||||||
|
seats: Record<string, PlayerSeatView>;
|
||||||
|
turnOrder: string[];
|
||||||
|
activePlayerId: string | null;
|
||||||
|
activeHandIndex: number;
|
||||||
|
phase: "betting" | "player_turns" | "resolved";
|
||||||
|
roundNumber: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlayerView extends BlackjackViewBase {
|
||||||
|
myPlayerId: string;
|
||||||
|
canAct: boolean;
|
||||||
|
canSplit: boolean;
|
||||||
|
canDoubleDown: boolean;
|
||||||
|
myCumulativePnl: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPlayerView(state: unknown): state is PlayerView {
|
||||||
|
return typeof state === "object" && state !== null && "canAct" in state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RANK_TO_FILENAME: Record<string, string> = {
|
||||||
|
A: "ace",
|
||||||
|
"2": "2",
|
||||||
|
"3": "3",
|
||||||
|
"4": "4",
|
||||||
|
"5": "5",
|
||||||
|
"6": "6",
|
||||||
|
"7": "7",
|
||||||
|
"8": "8",
|
||||||
|
"9": "9",
|
||||||
|
"10": "10",
|
||||||
|
J: "jack",
|
||||||
|
Q: "queen",
|
||||||
|
K: "king",
|
||||||
|
};
|
||||||
|
|
||||||
|
const CARD_FACE_STYLE = {
|
||||||
|
backfaceVisibility: "hidden",
|
||||||
|
WebkitBackfaceVisibility: "hidden",
|
||||||
|
} satisfies CSSProperties;
|
||||||
|
|
||||||
|
function cardImageUrl(card: Card): string {
|
||||||
|
const rank = RANK_TO_FILENAME[card.rank] ?? card.rank;
|
||||||
|
return `/cards/${rank}_of_${card.suit}.svg`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatAu(value: number): string {
|
||||||
|
return `${value} AU`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSignedAu(value: number): string {
|
||||||
|
return `${value > 0 ? "+" : ""}${value} AU`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function phaseLabel(phase: BlackjackViewBase["phase"]): string {
|
||||||
|
switch (phase) {
|
||||||
|
case "betting":
|
||||||
|
return "Betting Open";
|
||||||
|
case "player_turns":
|
||||||
|
return "Hands In Play";
|
||||||
|
case "resolved":
|
||||||
|
return "Round Settled";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function phaseDescription(
|
||||||
|
phase: BlackjackViewBase["phase"],
|
||||||
|
activePlayerName: string | null,
|
||||||
|
betAmount: number,
|
||||||
|
): string {
|
||||||
|
if (phase === "betting") {
|
||||||
|
return betAmount > 0
|
||||||
|
? `Table stake is ${formatAu(betAmount)}. Splits and doubles add the same amount.`
|
||||||
|
: "Free play table. Lock in when you are ready.";
|
||||||
|
}
|
||||||
|
if (phase === "player_turns") {
|
||||||
|
return activePlayerName ? `${activePlayerName} is on the clock.` : "Hands are live.";
|
||||||
|
}
|
||||||
|
return "Dealer has finished drawing. Review the results and queue the next round.";
|
||||||
|
}
|
||||||
|
|
||||||
|
function toneClasses(value: number): string {
|
||||||
|
if (value > 0) return "border-emerald-400/30 bg-emerald-500/10 text-emerald-300";
|
||||||
|
if (value < 0) return "border-red-400/30 bg-red-500/10 text-red-300";
|
||||||
|
return "border-white/10 bg-white/5 text-white/70";
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusChip(status: PlayerHandView["status"]): { label: string; className: string } {
|
||||||
|
switch (status) {
|
||||||
|
case "blackjack":
|
||||||
|
return { label: "Blackjack", className: "border-yellow-400/30 bg-yellow-400/15 text-yellow-100" };
|
||||||
|
case "bust":
|
||||||
|
return { label: "Bust", className: "border-red-400/30 bg-red-500/15 text-red-200" };
|
||||||
|
case "stood":
|
||||||
|
return { label: "Standing", className: "border-blue-400/30 bg-blue-500/15 text-blue-100" };
|
||||||
|
case "playing":
|
||||||
|
return { label: "Playing", className: "border-emerald-400/30 bg-emerald-500/15 text-emerald-100" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resultChip(result: PlayerHandView["result"]): { label: string; className: string; icon: LucideIcon } | null {
|
||||||
|
switch (result) {
|
||||||
|
case "blackjack":
|
||||||
|
return { label: "Paid 3:2", className: "border-yellow-400/30 bg-yellow-400/15 text-yellow-100", icon: Trophy };
|
||||||
|
case "win":
|
||||||
|
return { label: "Win", className: "border-emerald-400/30 bg-emerald-500/15 text-emerald-100", icon: ChevronsUp };
|
||||||
|
case "push":
|
||||||
|
return { label: "Push", className: "border-blue-400/30 bg-blue-500/15 text-blue-100", icon: Check };
|
||||||
|
case "lose":
|
||||||
|
return { label: "Lose", className: "border-red-400/30 bg-red-500/15 text-red-100", icon: AlertCircle };
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function PlayingCard({
|
||||||
|
card,
|
||||||
|
faceDown = false,
|
||||||
|
size = "normal",
|
||||||
|
index = 0,
|
||||||
|
}: {
|
||||||
|
card: Card;
|
||||||
|
faceDown?: boolean;
|
||||||
|
size?: "compact" | "normal";
|
||||||
|
index?: number;
|
||||||
|
}) {
|
||||||
|
const isHidden = faceDown || card.rank === "?";
|
||||||
|
const cardSize = size === "compact"
|
||||||
|
? "h-[4.6rem] w-[3.2rem] sm:h-[5rem] sm:w-[3.5rem]"
|
||||||
|
: "h-[5.4rem] w-[3.8rem] sm:h-[6.25rem] sm:w-[4.4rem]";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${cardSize} blackjack-card-deal relative shrink-0`}
|
||||||
|
style={{ perspective: "1200px", animationDelay: `${index * 70}ms` }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`relative h-full w-full rounded-xl transition-transform duration-500 [transform-style:preserve-3d] ${
|
||||||
|
isHidden ? "" : "[transform:rotateY(180deg)]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 overflow-hidden rounded-xl border border-black/30 shadow-[0_12px_24px_rgba(0,0,0,0.28)]"
|
||||||
|
style={CARD_FACE_STYLE}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/cards/back.png"
|
||||||
|
alt="Card back"
|
||||||
|
className="h-full w-full object-cover"
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 overflow-hidden rounded-xl border border-black/30 bg-white shadow-[0_12px_24px_rgba(0,0,0,0.28)]"
|
||||||
|
style={{ ...CARD_FACE_STYLE, transform: "rotateY(180deg)" }}
|
||||||
|
>
|
||||||
|
{!isHidden && (
|
||||||
|
<img
|
||||||
|
src={cardImageUrl(card)}
|
||||||
|
alt={`${card.rank} of ${card.suit}`}
|
||||||
|
className="h-full w-full object-contain"
|
||||||
|
draggable={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CardFan({
|
||||||
|
cards,
|
||||||
|
hiddenIndex = -1,
|
||||||
|
size = "normal",
|
||||||
|
}: {
|
||||||
|
cards: Card[];
|
||||||
|
hiddenIndex?: number;
|
||||||
|
size?: "compact" | "normal";
|
||||||
|
}) {
|
||||||
|
const overlap = size === "compact" ? "-ml-5 sm:-ml-6" : "-ml-6 sm:-ml-7";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-[5rem] items-end justify-center pl-6 sm:pl-7">
|
||||||
|
{cards.map((card, index) => (
|
||||||
|
<div key={`${card.rank}-${card.suit}-${index}`} className={index === 0 ? "" : overlap}>
|
||||||
|
<PlayingCard card={card} faceDown={index === hiddenIndex} size={size} index={index} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function InfoPill({ label, value }: { label: string; value: string }) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-full border border-white/10 bg-black/15 px-3 py-1.5 text-[11px] uppercase tracking-[0.2em] text-white/70">
|
||||||
|
<span className="text-white/45">{label}</span>
|
||||||
|
<span className="ml-2 text-white">{value}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetricCard({ label, value, hint, tone = "neutral" }: {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
hint?: string;
|
||||||
|
tone?: "neutral" | "positive" | "negative";
|
||||||
|
}) {
|
||||||
|
const toneClass = tone === "positive"
|
||||||
|
? "border-emerald-400/30 bg-emerald-500/10"
|
||||||
|
: tone === "negative"
|
||||||
|
? "border-red-400/30 bg-red-500/10"
|
||||||
|
: "border-white/10 bg-white/5";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`rounded-2xl border p-3 ${toneClass}`}>
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.18em] text-white/45">{label}</div>
|
||||||
|
<div className="mt-2 font-mono text-xl font-semibold text-white">{value}</div>
|
||||||
|
{hint && <div className="mt-1 text-xs text-white/55">{hint}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function HandPanel({
|
||||||
|
hand,
|
||||||
|
handIndex,
|
||||||
|
isActive,
|
||||||
|
showMoney,
|
||||||
|
}: {
|
||||||
|
hand: PlayerHandView;
|
||||||
|
handIndex: number;
|
||||||
|
isActive: boolean;
|
||||||
|
showMoney: boolean;
|
||||||
|
}) {
|
||||||
|
const status = statusChip(hand.status);
|
||||||
|
const outcome = resultChip(hand.result);
|
||||||
|
const net = hand.net ?? 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`rounded-2xl border p-3 transition-all ${
|
||||||
|
isActive
|
||||||
|
? "border-primary/45 bg-primary/10 shadow-[0_0_0_1px_rgba(233,195,73,0.14)]"
|
||||||
|
: "border-white/10 bg-black/15"
|
||||||
|
}`}>
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="text-sm font-semibold text-white">
|
||||||
|
{hand.fromSplit ? `Split Hand ${handIndex + 1}` : `Hand ${handIndex + 1}`}
|
||||||
|
</div>
|
||||||
|
<span className={`rounded-full border px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] ${status.className}`}>
|
||||||
|
{status.label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-full border border-white/10 bg-white/5 px-2.5 py-1 font-mono text-sm text-white">
|
||||||
|
{hand.value}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3">
|
||||||
|
<CardFan cards={hand.cards} size={hand.cards.length >= 4 ? "compact" : "normal"} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3 flex flex-wrap items-center gap-2">
|
||||||
|
{outcome && (
|
||||||
|
<span
|
||||||
|
className={`inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] ${outcome.className}`}
|
||||||
|
title={hand.resultReason ?? undefined}
|
||||||
|
>
|
||||||
|
<outcome.icon className="h-3 w-3" />
|
||||||
|
{outcome.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{hand.resultReason && (
|
||||||
|
<span className="text-xs text-white/55">{hand.resultReason}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showMoney && (
|
||||||
|
<div className="mt-3 grid grid-cols-3 gap-2 text-xs">
|
||||||
|
<div className="rounded-xl border border-white/10 bg-white/5 px-2 py-2">
|
||||||
|
<div className="text-white/45">Wager</div>
|
||||||
|
<div className="mt-1 font-mono text-white">{formatAu(hand.wager)}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-xl border border-white/10 bg-white/5 px-2 py-2">
|
||||||
|
<div className="text-white/45">Payout</div>
|
||||||
|
<div className="mt-1 font-mono text-white">{formatAu(hand.payout ?? 0)}</div>
|
||||||
|
</div>
|
||||||
|
<div className={`rounded-xl border px-2 py-2 ${toneClasses(net)}`}>
|
||||||
|
<div className="opacity-70">Net</div>
|
||||||
|
<div className="mt-1 font-mono">{formatSignedAu(net)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SeatPanel({
|
||||||
|
playerName,
|
||||||
|
seat,
|
||||||
|
isMe,
|
||||||
|
isActive,
|
||||||
|
activeHandIndex,
|
||||||
|
betAmount,
|
||||||
|
}: {
|
||||||
|
playerName: string;
|
||||||
|
seat: PlayerSeatView;
|
||||||
|
isMe: boolean;
|
||||||
|
isActive: boolean;
|
||||||
|
activeHandIndex: number;
|
||||||
|
betAmount: number;
|
||||||
|
}) {
|
||||||
|
const hasHands = seat.hands.length > 0;
|
||||||
|
const roundNet = seat.roundNet ?? 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`rounded-[26px] border p-4 transition-all ${
|
||||||
|
isActive
|
||||||
|
? "border-primary/45 bg-white/10 shadow-[0_18px_36px_rgba(0,0,0,0.22)]"
|
||||||
|
: "border-white/10 bg-black/15"
|
||||||
|
}`}>
|
||||||
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||||
|
<div className="flex min-w-0 items-center gap-3">
|
||||||
|
<div className={`flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl text-sm font-bold ${
|
||||||
|
isActive
|
||||||
|
? "bg-primary/20 text-primary ring-1 ring-primary/40"
|
||||||
|
: "bg-white/10 text-white"
|
||||||
|
}`}>
|
||||||
|
{playerName[0]?.toUpperCase() ?? "?"}
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<div className="truncate text-sm font-semibold text-white">{playerName}</div>
|
||||||
|
{isMe && <span className="rounded-full bg-primary/15 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-primary">You</span>}
|
||||||
|
{isActive && <span className="rounded-full bg-emerald-500/15 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-emerald-200">Turn</span>}
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 flex flex-wrap items-center gap-2 text-xs text-white/55">
|
||||||
|
{!hasHands && (
|
||||||
|
<span className={`rounded-full border px-2 py-0.5 ${seat.hasBet ? "border-emerald-400/30 bg-emerald-500/10 text-emerald-200" : "border-white/10 bg-white/5 text-white/55"}`}>
|
||||||
|
{seat.hasBet ? "Bet Locked" : "Waiting"}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{betAmount > 0 && seat.totalWager > 0 && <span>Wager {formatAu(seat.totalWager)}</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-right">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.18em] text-white/40">Total P&L</div>
|
||||||
|
<div className={`mt-1 inline-flex rounded-full border px-3 py-1 font-mono text-sm ${toneClasses(seat.cumulativePnl)}`}>
|
||||||
|
{formatSignedAu(seat.cumulativePnl)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{hasHands ? (
|
||||||
|
<div className="mt-4 grid gap-3">
|
||||||
|
{seat.hands.map((hand, handIndex) => (
|
||||||
|
<HandPanel
|
||||||
|
key={`${playerName}-${handIndex}`}
|
||||||
|
hand={hand}
|
||||||
|
handIndex={handIndex}
|
||||||
|
isActive={isActive && handIndex === activeHandIndex}
|
||||||
|
showMoney={betAmount > 0}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-4 rounded-2xl border border-dashed border-white/10 bg-black/10 px-4 py-6 text-center text-sm text-white/50">
|
||||||
|
{seat.hasBet
|
||||||
|
? "Ready for the deal."
|
||||||
|
: betAmount > 0
|
||||||
|
? `Waiting for ${formatAu(betAmount)} buy-in.`
|
||||||
|
: "Waiting for player to ready up."}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{seat.roundNet !== null && (
|
||||||
|
<div className="mt-3 flex items-center justify-between rounded-2xl border border-white/10 bg-black/15 px-3 py-2 text-xs">
|
||||||
|
<span className="uppercase tracking-[0.16em] text-white/45">Round</span>
|
||||||
|
<span className={`rounded-full border px-2.5 py-1 font-mono ${toneClasses(roundNet)}`}>
|
||||||
|
{formatSignedAu(roundNet)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EmptySeatCard({ onSit }: { onSit: () => void }) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={onSit}
|
||||||
|
className="flex h-full min-h-[250px] flex-col items-center justify-center rounded-[26px] border border-dashed border-primary/35 bg-primary/5 p-6 text-center transition hover:bg-primary/10"
|
||||||
|
>
|
||||||
|
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/15 text-primary">
|
||||||
|
<Plus className="h-5 w-5" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 text-lg font-semibold text-white">Open Seat</div>
|
||||||
|
<div className="mt-2 max-w-xs text-sm text-white/60">
|
||||||
|
Sit down during betting to join the next hand.
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MobileSeatRail({
|
||||||
|
seatedPlayers,
|
||||||
|
activePlayerId,
|
||||||
|
myPlayerId,
|
||||||
|
betAmount,
|
||||||
|
canSitDown,
|
||||||
|
onSit,
|
||||||
|
}: {
|
||||||
|
seatedPlayers: Array<{ playerId: string; seat: PlayerSeatView; name: string }>;
|
||||||
|
activePlayerId: string | null;
|
||||||
|
myPlayerId: string;
|
||||||
|
betAmount: number;
|
||||||
|
canSitDown: boolean;
|
||||||
|
onSit: () => void;
|
||||||
|
}) {
|
||||||
|
const seatCards = Array.from({ length: 6 }, (_, index) => {
|
||||||
|
const entry = seatedPlayers[index] ?? null;
|
||||||
|
if (!entry) return { kind: "empty" as const, index };
|
||||||
|
|
||||||
|
const { playerId, seat, name } = entry;
|
||||||
|
const isActive = activePlayerId === playerId;
|
||||||
|
const isMe = playerId === myPlayerId;
|
||||||
|
const summary = seat.roundNet !== null
|
||||||
|
? formatSignedAu(seat.roundNet)
|
||||||
|
: seat.hands.length > 0
|
||||||
|
? `${seat.hands.length} hand${seat.hands.length === 1 ? "" : "s"}`
|
||||||
|
: seat.hasBet
|
||||||
|
? "Bet locked"
|
||||||
|
: betAmount > 0
|
||||||
|
? "Needs buy-in"
|
||||||
|
: "Waiting";
|
||||||
|
|
||||||
|
return { kind: "seat" as const, index, playerId, name, seat, isActive, isMe, summary };
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="md:hidden">
|
||||||
|
<div className="flex items-center justify-between gap-3">
|
||||||
|
<div>
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-white/45">Table Seats</div>
|
||||||
|
<div className="mt-1 text-sm text-white/65">Fast seat scan for mobile play and spectating.</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-full border border-white/10 bg-black/15 px-3 py-1.5 text-xs text-white/70">
|
||||||
|
{seatedPlayers.length}/6 filled
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="-mx-1 mt-4 flex snap-x gap-3 overflow-x-auto px-1 pb-1">
|
||||||
|
{seatCards.map(card => (
|
||||||
|
card.kind === "seat" ? (
|
||||||
|
<div
|
||||||
|
key={card.playerId}
|
||||||
|
className={`min-w-[152px] snap-start rounded-[22px] border p-3 ${
|
||||||
|
card.isActive
|
||||||
|
? "border-primary/45 bg-white/10"
|
||||||
|
: "border-white/10 bg-black/15"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between gap-2">
|
||||||
|
<div className={`flex h-10 w-10 items-center justify-center rounded-2xl text-sm font-bold ${
|
||||||
|
card.isActive ? "bg-primary/20 text-primary" : "bg-white/10 text-white"
|
||||||
|
}`}>
|
||||||
|
{card.name[0]?.toUpperCase() ?? "?"}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-end gap-1">
|
||||||
|
{card.isMe && (
|
||||||
|
<span className="rounded-full bg-primary/15 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-primary">
|
||||||
|
You
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{card.isActive && (
|
||||||
|
<span className="rounded-full bg-emerald-500/15 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-emerald-200">
|
||||||
|
Acting
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 truncate text-sm font-semibold text-white">{card.name}</div>
|
||||||
|
<div className="mt-1 text-xs text-white/55">Seat {card.index + 1}</div>
|
||||||
|
<div className="mt-3 rounded-2xl border border-white/10 bg-black/15 px-3 py-2">
|
||||||
|
<div className="text-[10px] uppercase tracking-[0.16em] text-white/40">Status</div>
|
||||||
|
<div className={`mt-1 inline-flex rounded-full border px-2.5 py-1 text-xs ${toneClasses(card.seat.roundNet ?? card.seat.cumulativePnl)}`}>
|
||||||
|
{card.summary}
|
||||||
|
</div>
|
||||||
|
{betAmount > 0 && card.seat.totalWager > 0 && (
|
||||||
|
<div className="mt-2 text-xs text-white/60">Wager {formatAu(card.seat.totalWager)}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
key={`empty-${card.index}`}
|
||||||
|
className="min-w-[152px] snap-start rounded-[22px] border border-dashed border-white/10 bg-black/10 p-3"
|
||||||
|
>
|
||||||
|
<div className="flex h-10 w-10 items-center justify-center rounded-2xl bg-white/5 text-white/50">
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 text-sm font-semibold text-white">Open Seat</div>
|
||||||
|
<div className="mt-1 text-xs text-white/50">Seat {card.index + 1}</div>
|
||||||
|
{canSitDown ? (
|
||||||
|
<button
|
||||||
|
onClick={onSit}
|
||||||
|
className="mt-4 inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-primary px-3 py-2 text-xs font-semibold text-on-primary transition hover:opacity-90"
|
||||||
|
>
|
||||||
|
<Plus className="h-3.5 w-3.5" />
|
||||||
|
Sit Down
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div className="mt-4 rounded-2xl border border-white/10 bg-black/15 px-3 py-2 text-xs text-white/55">
|
||||||
|
Available next betting round.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DealerPanel({ hand, visibleValue, fullValue }: {
|
||||||
|
hand: Card[];
|
||||||
|
visibleValue: number;
|
||||||
|
fullValue: number | null;
|
||||||
|
}) {
|
||||||
|
const hiddenIndex = fullValue === null && hand.length > 1 ? 1 : -1;
|
||||||
|
const shownValue = fullValue ?? visibleValue;
|
||||||
|
const isBust = fullValue !== null && fullValue > 21;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="rounded-[28px] border border-white/10 bg-black/20 p-4 sm:p-5">
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||||
|
<div>
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.22em] text-primary/70">Dealer</div>
|
||||||
|
<div className="mt-1 text-lg font-semibold text-white">
|
||||||
|
{fullValue === null ? "Hole card hidden" : isBust ? "Dealer busts" : "House stands"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={`rounded-full border px-3 py-1.5 font-mono text-sm ${
|
||||||
|
fullValue === 21
|
||||||
|
? "border-yellow-400/40 bg-yellow-400/15 text-yellow-100"
|
||||||
|
: isBust
|
||||||
|
? "border-red-400/40 bg-red-500/15 text-red-100"
|
||||||
|
: "border-white/10 bg-white/5 text-white"
|
||||||
|
}`}>
|
||||||
|
{fullValue === null ? `${shownValue} + hidden` : shownValue}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 flex justify-center">
|
||||||
|
<CardFan cards={hand} hiddenIndex={hiddenIndex} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SidebarCard({ title, subtitle, children }: {
|
||||||
|
title: string;
|
||||||
|
subtitle?: string;
|
||||||
|
children: ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-[26px] border border-white/10 bg-card/75 p-4 backdrop-blur">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.2em] text-white/45">{title}</div>
|
||||||
|
{subtitle && <div className="mt-1 text-sm text-white/65">{subtitle}</div>}
|
||||||
|
<div className="mt-4">{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BlackjackGame({ state, myPlayerId, isSpectator, onAction, players, roundResult, roomOptions }: GameUIProps) {
|
||||||
|
const view = state as PlayerView | BlackjackViewBase;
|
||||||
|
const playerView = isPlayerView(state) ? state : null;
|
||||||
|
const betAmount = roomOptions?.betAmount ?? 0;
|
||||||
|
const isBetting = view.phase === "betting";
|
||||||
|
const isPlaying = view.phase === "player_turns";
|
||||||
|
const isResolved = view.phase === "resolved";
|
||||||
|
const mySeat = view.seats[myPlayerId];
|
||||||
|
const myBetPlaced = mySeat?.hasBet ?? false;
|
||||||
|
const canSitDown = isSpectator && isBetting && view.turnOrder.length < 6;
|
||||||
|
|
||||||
|
const getPlayerName = useMemo(() => {
|
||||||
|
const names = new Map(players.map(player => [player.discordId, player.username]));
|
||||||
|
return (playerId: string) => names.get(playerId) ?? playerId.slice(0, 8);
|
||||||
|
}, [players]);
|
||||||
|
|
||||||
|
const seatedPlayers = useMemo(() => (
|
||||||
|
view.turnOrder.map(playerId => ({
|
||||||
|
playerId,
|
||||||
|
seat: view.seats[playerId],
|
||||||
|
name: getPlayerName(playerId),
|
||||||
|
})).filter((entry): entry is { playerId: string; seat: PlayerSeatView; name: string } => Boolean(entry.seat))
|
||||||
|
), [getPlayerName, view.seats, view.turnOrder]);
|
||||||
|
|
||||||
|
const activePlayerName = view.activePlayerId ? getPlayerName(view.activePlayerId) : null;
|
||||||
|
const mySettlement = roundResult?.settlements?.[myPlayerId]
|
||||||
|
?? (mySeat && mySeat.roundNet !== null
|
||||||
|
? {
|
||||||
|
wager: mySeat.totalWager,
|
||||||
|
payout: mySeat.totalWager + mySeat.roundNet,
|
||||||
|
net: mySeat.roundNet,
|
||||||
|
}
|
||||||
|
: null);
|
||||||
|
const totalPnl = playerView?.myCumulativePnl ?? mySeat?.cumulativePnl ?? 0;
|
||||||
|
|
||||||
|
const [showNextRound, setShowNextRound] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isResolved) {
|
||||||
|
setShowNextRound(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timer = setTimeout(() => setShowNextRound(true), 1800);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [isResolved, view.roundNumber]);
|
||||||
|
|
||||||
|
const handlePlaceBet = useCallback(() => onAction({ type: "place_bet" }), [onAction]);
|
||||||
|
const handleHit = useCallback(() => onAction({ type: "hit" }), [onAction]);
|
||||||
|
const handleStand = useCallback(() => onAction({ type: "stand" }), [onAction]);
|
||||||
|
const handleSplit = useCallback(() => onAction({ type: "split" }), [onAction]);
|
||||||
|
const handleDouble = useCallback(() => onAction({ type: "double_down" }), [onAction]);
|
||||||
|
const handleLeave = useCallback(() => onAction({ type: "leave_table" }), [onAction]);
|
||||||
|
const handleSit = useCallback(() => onAction({ type: "sit_down" }), [onAction]);
|
||||||
|
|
||||||
|
const primaryButtonClass = "inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-primary px-4 py-3 text-sm font-semibold text-on-primary transition hover:opacity-90";
|
||||||
|
const secondaryButtonClass = "inline-flex w-full items-center justify-center gap-2 rounded-2xl border border-white/10 bg-white/5 px-4 py-3 text-sm font-semibold text-white transition hover:bg-white/10";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto flex w-full max-w-7xl flex-col gap-4">
|
||||||
|
<div className="grid gap-4 xl:grid-cols-[minmax(0,1fr)_340px]">
|
||||||
|
<div
|
||||||
|
className="relative overflow-hidden rounded-[32px] border border-emerald-950/70 p-4 sm:p-5 lg:p-6"
|
||||||
|
style={{
|
||||||
|
backgroundImage: [
|
||||||
|
"radial-gradient(circle at top, rgba(34,197,94,0.18), transparent 35%)",
|
||||||
|
"radial-gradient(circle at bottom, rgba(16,185,129,0.14), transparent 45%)",
|
||||||
|
"linear-gradient(180deg, #14532d 0%, #064e3b 48%, #022c22 100%)",
|
||||||
|
].join(", "),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="pointer-events-none absolute inset-0 opacity-25" style={{
|
||||||
|
backgroundImage: "linear-gradient(135deg, rgba(255,255,255,0.04) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.04) 50%, rgba(255,255,255,0.04) 75%, transparent 75%, transparent)",
|
||||||
|
backgroundSize: "28px 28px",
|
||||||
|
}} />
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<div className="flex flex-wrap items-start justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.24em] text-primary/80">Aurora Casino</div>
|
||||||
|
<div className="mt-1 font-display text-3xl font-semibold text-white sm:text-4xl">Blackjack</div>
|
||||||
|
<p className="mt-2 max-w-2xl text-sm text-white/70">
|
||||||
|
{phaseDescription(view.phase, activePlayerName, betAmount)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap items-center justify-end gap-2">
|
||||||
|
<InfoPill label="Round" value={String(view.roundNumber)} />
|
||||||
|
<InfoPill label="Phase" value={phaseLabel(view.phase)} />
|
||||||
|
<InfoPill label="Seats" value={`${view.turnOrder.length}/6`} />
|
||||||
|
<InfoPill label="Stake" value={betAmount > 0 ? formatAu(betAmount) : "Free"} />
|
||||||
|
{!isSpectator && (
|
||||||
|
<button
|
||||||
|
onClick={handleLeave}
|
||||||
|
className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-black/20 px-3 py-2 text-sm text-white/70 transition hover:bg-black/30 hover:text-white"
|
||||||
|
>
|
||||||
|
<LogOut className="h-4 w-4" />
|
||||||
|
Leave
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5">
|
||||||
|
<DealerPanel hand={view.dealerHand} visibleValue={view.dealerVisibleValue} fullValue={view.dealerFullValue} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 rounded-[24px] border border-white/10 bg-black/15 px-4 py-3">
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<div className="flex items-center gap-2 text-sm text-white">
|
||||||
|
<Eye className="h-4 w-4 text-primary" />
|
||||||
|
<span>{isPlaying && activePlayerName ? `${activePlayerName} to act` : phaseLabel(view.phase)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-white/55">
|
||||||
|
{betAmount > 0
|
||||||
|
? "Push refunds the wager. Blackjack pays 3:2."
|
||||||
|
: "Free table mode does not affect balance."}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5">
|
||||||
|
<MobileSeatRail
|
||||||
|
seatedPlayers={seatedPlayers}
|
||||||
|
activePlayerId={view.activePlayerId}
|
||||||
|
myPlayerId={myPlayerId}
|
||||||
|
betAmount={betAmount}
|
||||||
|
canSitDown={canSitDown}
|
||||||
|
onSit={handleSit}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-5 grid gap-4 md:grid-cols-2 2xl:grid-cols-3">
|
||||||
|
{seatedPlayers.map(({ playerId, seat, name }) => (
|
||||||
|
<SeatPanel
|
||||||
|
key={playerId}
|
||||||
|
playerName={name}
|
||||||
|
seat={seat}
|
||||||
|
isMe={playerId === myPlayerId}
|
||||||
|
isActive={view.activePlayerId === playerId}
|
||||||
|
activeHandIndex={view.activePlayerId === playerId ? view.activeHandIndex : -1}
|
||||||
|
betAmount={betAmount}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{canSitDown && <EmptySeatCard onSit={handleSit} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
{!isSpectator && (
|
||||||
|
<SidebarCard
|
||||||
|
title="Seat Summary"
|
||||||
|
subtitle={mySeat ? "Your bankroll view updates from the resolved hand data." : "You are not seated at the table."}
|
||||||
|
>
|
||||||
|
{mySeat ? (
|
||||||
|
<div className="grid gap-3 sm:grid-cols-3 xl:grid-cols-1">
|
||||||
|
<MetricCard label="Table Stake" value={betAmount > 0 ? formatAu(betAmount) : "Free"} hint={betAmount > 0 ? "Applied per new hand." : "Practice table."} />
|
||||||
|
<MetricCard label="Round Wager" value={formatAu(mySeat.totalWager)} hint={mySeat.totalWager > betAmount ? "Split or double included." : "Current buy-in."} />
|
||||||
|
<MetricCard
|
||||||
|
label="Total P&L"
|
||||||
|
value={formatSignedAu(totalPnl)}
|
||||||
|
hint="Running total across completed rounds."
|
||||||
|
tone={totalPnl > 0 ? "positive" : totalPnl < 0 ? "negative" : "neutral"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="rounded-2xl border border-dashed border-white/10 bg-white/5 px-4 py-6 text-sm text-white/55">
|
||||||
|
Leave and rejoin if you need to reclaim a missing seat.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SidebarCard>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<SidebarCard
|
||||||
|
title="Round Result"
|
||||||
|
subtitle={isResolved ? "Settlement is based on the exact wager committed by each hand." : "Result details appear here after the dealer resolves."}
|
||||||
|
>
|
||||||
|
{mySettlement ? (
|
||||||
|
<div className="grid gap-3">
|
||||||
|
<MetricCard label="Wager" value={formatAu(mySettlement.wager)} />
|
||||||
|
<MetricCard label="Payout" value={formatAu(mySettlement.payout)} />
|
||||||
|
<MetricCard
|
||||||
|
label="Net"
|
||||||
|
value={formatSignedAu(mySettlement.net)}
|
||||||
|
tone={mySettlement.net > 0 ? "positive" : mySettlement.net < 0 ? "negative" : "neutral"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="rounded-2xl border border-dashed border-white/10 bg-white/5 px-4 py-6 text-sm text-white/55">
|
||||||
|
No result locked in yet.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SidebarCard>
|
||||||
|
|
||||||
|
<SidebarCard
|
||||||
|
title={isSpectator ? "Spectator Controls" : "Table Controls"}
|
||||||
|
subtitle={
|
||||||
|
isSpectator
|
||||||
|
? canSitDown ? "You can still grab the next available seat." : "You are watching the current hand."
|
||||||
|
: isBetting
|
||||||
|
? "Lock your wager to start the deal."
|
||||||
|
: isPlaying
|
||||||
|
? playerView?.canAct ? "Only the active hand can submit controls." : "Waiting for another player."
|
||||||
|
: "Queue up the next round once the settlement banner lands."
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{!isSpectator && isBetting && !myBetPlaced && (
|
||||||
|
<button onClick={handlePlaceBet} className={primaryButtonClass}>
|
||||||
|
<Coins className="h-4 w-4" />
|
||||||
|
{betAmount > 0 ? `Place Bet · ${formatAu(betAmount)}` : "Ready Up"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSpectator && isBetting && myBetPlaced && (
|
||||||
|
<div className="rounded-2xl border border-emerald-400/20 bg-emerald-500/10 px-4 py-3 text-sm text-emerald-100">
|
||||||
|
Your wager is locked. Waiting for the rest of the table.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSpectator && isPlaying && playerView?.canAct && (
|
||||||
|
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-1">
|
||||||
|
<button onClick={handleHit} className={primaryButtonClass}>
|
||||||
|
<Hand className="h-4 w-4" />
|
||||||
|
Hit
|
||||||
|
</button>
|
||||||
|
<button onClick={handleStand} className={secondaryButtonClass}>
|
||||||
|
<Square className="h-4 w-4" />
|
||||||
|
Stand
|
||||||
|
</button>
|
||||||
|
{playerView.canSplit && (
|
||||||
|
<button onClick={handleSplit} className={secondaryButtonClass}>
|
||||||
|
<ArrowLeftRight className="h-4 w-4" />
|
||||||
|
Split{betAmount > 0 ? ` · +${formatAu(betAmount)}` : ""}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{playerView.canDoubleDown && (
|
||||||
|
<button onClick={handleDouble} className={secondaryButtonClass}>
|
||||||
|
<ChevronsUp className="h-4 w-4" />
|
||||||
|
Double{betAmount > 0 ? ` · +${formatAu(betAmount)}` : ""}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSpectator && isPlaying && playerView && !playerView.canAct && (
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-white/5 px-4 py-3 text-sm text-white/70">
|
||||||
|
{activePlayerName ? `Waiting for ${activePlayerName}.` : "Waiting for the table."}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSpectator && isResolved && showNextRound && (
|
||||||
|
<button onClick={handlePlaceBet} className={primaryButtonClass}>
|
||||||
|
<Coins className="h-4 w-4" />
|
||||||
|
{betAmount > 0 ? `Next Round · ${formatAu(betAmount)}` : "Next Round"}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSpectator && isResolved && !showNextRound && (
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-white/5 px-4 py-3 text-sm text-white/70">
|
||||||
|
Settlement syncing. Next round opens in a moment.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isSpectator && canSitDown && (
|
||||||
|
<button onClick={handleSit} className={primaryButtonClass}>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Sit Down For Next Hand
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isSpectator && !canSitDown && (
|
||||||
|
<div className="rounded-2xl border border-white/10 bg-white/5 px-4 py-3 text-sm text-white/70">
|
||||||
|
{activePlayerName ? `${activePlayerName} is currently acting.` : "Table is in motion."}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SidebarCard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
951
panel/src/games/chess/ChessGame.tsx
Normal file
@@ -0,0 +1,951 @@
|
|||||||
|
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
||||||
|
import { Chessboard } from "react-chessboard";
|
||||||
|
import { Chess } from "chess.js";
|
||||||
|
import type { Square } from "chess.js";
|
||||||
|
import { Check, Clock3, Eye, Flag, Handshake, RotateCw, TimerReset, X } from "lucide-react";
|
||||||
|
import type { GameUIProps } from "../registry";
|
||||||
|
import { chessPieces } from "./pieces";
|
||||||
|
import { CHESS_TIME_CONTROL_LABELS } from "./timeControls";
|
||||||
|
|
||||||
|
interface ChessClockView {
|
||||||
|
white: number;
|
||||||
|
black: number;
|
||||||
|
increment: number;
|
||||||
|
activeColor: "white" | "black" | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChessViewBase {
|
||||||
|
fen: string;
|
||||||
|
pgn: string;
|
||||||
|
turn: "white" | "black";
|
||||||
|
clock: ChessClockView | null;
|
||||||
|
drawOffer: "white" | "black" | null;
|
||||||
|
result: "white" | "black" | "draw" | null;
|
||||||
|
resultReason: string | null;
|
||||||
|
moveHistory: { from: string; to: string; san: string; color: "w" | "b" }[];
|
||||||
|
isCheck: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PlayerView extends ChessViewBase {
|
||||||
|
myColor: "white" | "black";
|
||||||
|
legalMoves: { from: string; to: string; promotion?: string }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpectatorView extends ChessViewBase {
|
||||||
|
players: { white: string; black: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MoveIntent {
|
||||||
|
from: Square;
|
||||||
|
to: Square;
|
||||||
|
promotion?: string;
|
||||||
|
mode: "move" | "premove";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocalNotice {
|
||||||
|
tone: "info" | "success" | "warning";
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FILES = ["a", "b", "c", "d", "e", "f", "g", "h"] as const;
|
||||||
|
function isPlayerView(state: unknown): state is PlayerView {
|
||||||
|
return typeof state === "object" && state !== null && "myColor" in state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(ms: number): string {
|
||||||
|
if (ms <= 0) return "0:00";
|
||||||
|
const totalSeconds = Math.ceil(ms / 1000);
|
||||||
|
const minutes = Math.floor(totalSeconds / 60);
|
||||||
|
const seconds = totalSeconds % 60;
|
||||||
|
if (minutes >= 60) {
|
||||||
|
const hours = Math.floor(minutes / 60);
|
||||||
|
const mins = minutes % 60;
|
||||||
|
return `${hours}:${mins.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function colorLabel(color: "white" | "black"): string {
|
||||||
|
return color === "white" ? "White" : "Black";
|
||||||
|
}
|
||||||
|
|
||||||
|
function squareToCoords(square: Square): [number, number] {
|
||||||
|
return [square.charCodeAt(0) - 97, Number(square[1]) - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function coordsToSquare(file: number, rank: number): Square | null {
|
||||||
|
if (file < 0 || file > 7 || rank < 0 || rank > 7) return null;
|
||||||
|
return `${FILES[file]}${rank + 1}` as Square;
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendBoxShadow(style: React.CSSProperties | undefined, shadow: string): React.CSSProperties {
|
||||||
|
return {
|
||||||
|
...style,
|
||||||
|
boxShadow: style?.boxShadow ? `${style.boxShadow}, ${shadow}` : shadow,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMoveIntent(intent: Pick<MoveIntent, "from" | "to" | "promotion">): string {
|
||||||
|
const promotion = intent.promotion ? `=${intent.promotion.toUpperCase()}` : "";
|
||||||
|
return `${intent.from.toUpperCase()}-${intent.to.toUpperCase()}${promotion}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPromotionMove(pieceType: string | undefined, pieceColor: "w" | "b" | undefined, to: Square): boolean {
|
||||||
|
if (pieceType !== "p" || !pieceColor) return false;
|
||||||
|
return (pieceColor === "w" && to[1] === "8") || (pieceColor === "b" && to[1] === "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPremoveTargets(game: Chess, from: Square, myColor: "white" | "black"): Square[] {
|
||||||
|
const piece = game.get(from);
|
||||||
|
const colorCode = myColor === "white" ? "w" : "b";
|
||||||
|
if (!piece || piece.color !== colorCode) return [];
|
||||||
|
|
||||||
|
const targets = new Set<Square>();
|
||||||
|
const [file, rank] = squareToCoords(from);
|
||||||
|
const add = (target: Square | null) => {
|
||||||
|
if (!target) return false;
|
||||||
|
const occupant = game.get(target);
|
||||||
|
if (occupant?.color === colorCode) return false;
|
||||||
|
targets.add(target);
|
||||||
|
return occupant == null;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (piece.type) {
|
||||||
|
case "n": {
|
||||||
|
for (const [df, dr] of [[1, 2], [2, 1], [2, -1], [1, -2], [-1, -2], [-2, -1], [-2, 1], [-1, 2]] as const) {
|
||||||
|
add(coordsToSquare(file + df, rank + dr));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "k": {
|
||||||
|
for (const [df, dr] of [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]] as const) {
|
||||||
|
add(coordsToSquare(file + df, rank + dr));
|
||||||
|
}
|
||||||
|
|
||||||
|
const castling = game.fen().split(" ")[2] ?? "-";
|
||||||
|
if (myColor === "white") {
|
||||||
|
if (castling.includes("K") && game.get("f1")?.color !== colorCode && game.get("g1")?.color !== colorCode) {
|
||||||
|
targets.add("g1");
|
||||||
|
}
|
||||||
|
if (castling.includes("Q") && game.get("b1")?.color !== colorCode && game.get("c1")?.color !== colorCode && game.get("d1")?.color !== colorCode) {
|
||||||
|
targets.add("c1");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (castling.includes("k") && game.get("f8")?.color !== colorCode && game.get("g8")?.color !== colorCode) {
|
||||||
|
targets.add("g8");
|
||||||
|
}
|
||||||
|
if (castling.includes("q") && game.get("b8")?.color !== colorCode && game.get("c8")?.color !== colorCode && game.get("d8")?.color !== colorCode) {
|
||||||
|
targets.add("c8");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "p": {
|
||||||
|
const direction = myColor === "white" ? 1 : -1;
|
||||||
|
const startRank = myColor === "white" ? 1 : 6;
|
||||||
|
const oneForward = coordsToSquare(file, rank + direction);
|
||||||
|
if (add(oneForward) && rank === startRank) {
|
||||||
|
add(coordsToSquare(file, rank + direction * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
add(coordsToSquare(file - 1, rank + direction));
|
||||||
|
add(coordsToSquare(file + 1, rank + direction));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "b":
|
||||||
|
case "r":
|
||||||
|
case "q": {
|
||||||
|
const directions: Array<[number, number]> = [];
|
||||||
|
if (piece.type === "b" || piece.type === "q") directions.push([1, 1], [1, -1], [-1, 1], [-1, -1]);
|
||||||
|
if (piece.type === "r" || piece.type === "q") directions.push([1, 0], [-1, 0], [0, 1], [0, -1]);
|
||||||
|
|
||||||
|
for (const [df, dr] of directions) {
|
||||||
|
let nextFile = file + df;
|
||||||
|
let nextRank = rank + dr;
|
||||||
|
while (true) {
|
||||||
|
const target = coordsToSquare(nextFile, nextRank);
|
||||||
|
if (!target) break;
|
||||||
|
const shouldContinue = add(target);
|
||||||
|
if (!shouldContinue) break;
|
||||||
|
nextFile += df;
|
||||||
|
nextRank += dr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PlayerCard({
|
||||||
|
name,
|
||||||
|
color,
|
||||||
|
isTurn,
|
||||||
|
isSelf,
|
||||||
|
time,
|
||||||
|
clockActive,
|
||||||
|
clockLow,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
color: "white" | "black";
|
||||||
|
isTurn: boolean;
|
||||||
|
isSelf: boolean;
|
||||||
|
time: number | null;
|
||||||
|
clockActive: boolean;
|
||||||
|
clockLow: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={`w-full rounded-2xl border px-4 py-3 transition-colors ${
|
||||||
|
isTurn
|
||||||
|
? "border-primary/35 bg-primary/10"
|
||||||
|
: "border-border/60 bg-card/80"
|
||||||
|
}`}>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className={`h-3.5 w-3.5 rounded-full shrink-0 ${color === "white" ? "bg-white ring-1 ring-zinc-300" : "bg-zinc-900 ring-1 ring-zinc-500"}`} />
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div className="flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
<span>{colorLabel(color)}</span>
|
||||||
|
{isSelf && <span className="rounded-full bg-primary/15 px-2 py-0.5 text-primary">You</span>}
|
||||||
|
{isTurn && <span className="rounded-full bg-success/15 px-2 py-0.5 text-success">Turn</span>}
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 truncate text-sm font-semibold text-foreground">{name}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{time != null && (
|
||||||
|
<div className={`rounded-xl border px-3 py-2 text-right transition-colors ${
|
||||||
|
clockActive
|
||||||
|
? clockLow
|
||||||
|
? "border-destructive/35 bg-destructive/10 text-destructive"
|
||||||
|
: "border-primary/35 bg-primary/10 text-primary"
|
||||||
|
: "border-border/60 bg-raised text-text-secondary"
|
||||||
|
}`}>
|
||||||
|
<div className="flex items-center justify-end gap-1.5 text-[11px] uppercase tracking-[0.14em] opacity-80">
|
||||||
|
<Clock3 className="h-3.5 w-3.5" />
|
||||||
|
<span>Clock</span>
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 font-mono text-lg font-semibold tabular-nums">{formatTime(time)}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MoveHistory({
|
||||||
|
moves,
|
||||||
|
containerRef,
|
||||||
|
}: {
|
||||||
|
moves: { san: string; color: "w" | "b" }[];
|
||||||
|
containerRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
}) {
|
||||||
|
const pairs: { number: number; white?: string; black?: string; isLatest: boolean }[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < moves.length; i++) {
|
||||||
|
const moveNumber = Math.floor(i / 2) + 1;
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
pairs.push({
|
||||||
|
number: moveNumber,
|
||||||
|
white: moves[i]!.san,
|
||||||
|
isLatest: i === moves.length - 1,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const pair = pairs[pairs.length - 1];
|
||||||
|
if (pair) {
|
||||||
|
pair.black = moves[i]!.san;
|
||||||
|
pair.isLatest = i === moves.length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (containerRef.current) {
|
||||||
|
containerRef.current.scrollTop = containerRef.current.scrollHeight;
|
||||||
|
}
|
||||||
|
}, [moves.length, containerRef]);
|
||||||
|
|
||||||
|
if (pairs.length === 0) {
|
||||||
|
return (
|
||||||
|
<div className="py-8 text-center text-sm text-text-disabled">
|
||||||
|
No moves yet
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
{pairs.map(pair => (
|
||||||
|
<div
|
||||||
|
key={pair.number}
|
||||||
|
className={`grid grid-cols-[auto_1fr_1fr] items-center gap-x-3 rounded-xl px-2 py-1.5 transition-colors ${
|
||||||
|
pair.isLatest ? "bg-primary/10" : "bg-transparent"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="text-xs tabular-nums text-text-disabled">{pair.number}.</span>
|
||||||
|
<span className={`min-w-0 truncate font-mono text-sm ${pair.white ? "text-foreground" : "text-text-disabled"}`}>
|
||||||
|
{pair.white ?? ""}
|
||||||
|
</span>
|
||||||
|
<span className={`min-w-0 truncate font-mono text-sm ${pair.black ? "text-foreground" : "text-text-disabled"}`}>
|
||||||
|
{pair.black ?? ""}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChessGame({ state, myPlayerId, isSpectator, onAction, players, roomOptions }: GameUIProps) {
|
||||||
|
const view = state as PlayerView | SpectatorView;
|
||||||
|
const playerView = isPlayerView(state) ? state : null;
|
||||||
|
const myColor = playerView?.myColor ?? "white";
|
||||||
|
const isSoloMode = !isSpectator && players.length === 2 && players[0]?.discordId === players[1]?.discordId;
|
||||||
|
const defaultOrientation: "white" | "black" = isSpectator || isSoloMode ? "white" : myColor;
|
||||||
|
const [boardOrientation, setBoardOrientation] = useState<"white" | "black">(defaultOrientation);
|
||||||
|
const isMyTurn = playerView ? view.turn === playerView.myColor : false;
|
||||||
|
const isGameOver = view.result !== null;
|
||||||
|
const moveHistoryRef = useRef<HTMLDivElement>(null);
|
||||||
|
const board = useMemo(() => new Chess(view.fen), [view.fen]);
|
||||||
|
|
||||||
|
const [selectedSquare, setSelectedSquare] = useState<Square | null>(null);
|
||||||
|
const [pendingPromotion, setPendingPromotion] = useState<MoveIntent | null>(null);
|
||||||
|
const [queuedMove, setQueuedMove] = useState<MoveIntent | null>(null);
|
||||||
|
const [liveClock, setLiveClock] = useState(view.clock);
|
||||||
|
const [localNotice, setLocalNotice] = useState<LocalNotice | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setBoardOrientation(defaultOrientation);
|
||||||
|
}, [defaultOrientation]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLiveClock(view.clock);
|
||||||
|
}, [view.clock?.white, view.clock?.black, view.clock?.activeColor]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!liveClock || !liveClock.activeColor || isGameOver) return;
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setLiveClock(prev => {
|
||||||
|
if (!prev || !prev.activeColor) return prev;
|
||||||
|
const activeColor = prev.activeColor;
|
||||||
|
const remaining = Math.max(0, prev[activeColor] - 100);
|
||||||
|
|
||||||
|
if (remaining <= 0 && !isSpectator && activeColor !== myColor) {
|
||||||
|
onAction({ type: "claim_timeout" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...prev, [activeColor]: remaining };
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [liveClock?.activeColor, isGameOver, isSpectator, myColor, onAction]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!localNotice) return;
|
||||||
|
const timeout = setTimeout(() => setLocalNotice(null), 2500);
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, [localNotice]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!playerView || !queuedMove || !isMyTurn || isGameOver) return;
|
||||||
|
|
||||||
|
const isLegal = playerView.legalMoves.some(move =>
|
||||||
|
move.from === queuedMove.from &&
|
||||||
|
move.to === queuedMove.to &&
|
||||||
|
(queuedMove.promotion ? move.promotion === queuedMove.promotion : !move.promotion),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isLegal) {
|
||||||
|
const moveToSend = queuedMove;
|
||||||
|
setQueuedMove(null);
|
||||||
|
setSelectedSquare(null);
|
||||||
|
setLocalNotice({ tone: "success", text: `Premove played: ${formatMoveIntent(moveToSend)}` });
|
||||||
|
onAction({ type: "move", from: moveToSend.from, to: moveToSend.to, promotion: moveToSend.promotion });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setQueuedMove(null);
|
||||||
|
setLocalNotice({ tone: "warning", text: "Premove cancelled because the position changed." });
|
||||||
|
}, [playerView, queuedMove, isMyTurn, isGameOver, onAction]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedSquare(null);
|
||||||
|
setPendingPromotion(null);
|
||||||
|
}, [view.turn]);
|
||||||
|
|
||||||
|
const selectedTargets = useMemo(() => {
|
||||||
|
if (!playerView || !selectedSquare || isGameOver) return [];
|
||||||
|
|
||||||
|
if (isMyTurn) {
|
||||||
|
return playerView.legalMoves
|
||||||
|
.filter(move => move.from === selectedSquare)
|
||||||
|
.map(move => move.to as Square);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getPremoveTargets(board, selectedSquare, myColor);
|
||||||
|
}, [playerView, selectedSquare, isGameOver, isMyTurn, board, myColor]);
|
||||||
|
|
||||||
|
const customSquareStyles = useMemo(() => {
|
||||||
|
const styles: Record<string, React.CSSProperties> = {};
|
||||||
|
const latestMove = view.moveHistory.length > 0 ? view.moveHistory[view.moveHistory.length - 1] : null;
|
||||||
|
|
||||||
|
if (latestMove) {
|
||||||
|
styles[latestMove.from] = { ...styles[latestMove.from], backgroundColor: "rgba(233, 195, 73, 0.16)" };
|
||||||
|
styles[latestMove.to] = { ...styles[latestMove.to], backgroundColor: "rgba(233, 195, 73, 0.24)" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queuedMove) {
|
||||||
|
styles[queuedMove.from] = appendBoxShadow(styles[queuedMove.from], "inset 0 0 0 3px rgba(59, 130, 246, 0.65)");
|
||||||
|
styles[queuedMove.to] = appendBoxShadow(styles[queuedMove.to], "inset 0 0 0 3px rgba(59, 130, 246, 0.9)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedSquare) {
|
||||||
|
styles[selectedSquare] = appendBoxShadow(styles[selectedSquare], `inset 0 0 0 3px ${
|
||||||
|
isMyTurn ? "rgba(233, 195, 73, 0.95)" : "rgba(59, 130, 246, 0.9)"
|
||||||
|
}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const target of selectedTargets) {
|
||||||
|
const occupant = board.get(target);
|
||||||
|
if (occupant) {
|
||||||
|
styles[target] = appendBoxShadow(styles[target], `inset 0 0 0 3px ${
|
||||||
|
isMyTurn ? "rgba(233, 195, 73, 0.55)" : "rgba(59, 130, 246, 0.55)"
|
||||||
|
}`);
|
||||||
|
} else {
|
||||||
|
styles[target] = {
|
||||||
|
...styles[target],
|
||||||
|
backgroundImage: `radial-gradient(circle, ${
|
||||||
|
isMyTurn ? "rgba(233, 195, 73, 0.38)" : "rgba(59, 130, 246, 0.36)"
|
||||||
|
} 21%, transparent 23%)`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view.isCheck && !isGameOver) {
|
||||||
|
const checkedColor = view.turn === "white" ? "w" : "b";
|
||||||
|
for (const row of board.board()) {
|
||||||
|
for (const piece of row) {
|
||||||
|
if (piece && piece.type === "k" && piece.color === checkedColor) {
|
||||||
|
styles[piece.square] = {
|
||||||
|
...styles[piece.square],
|
||||||
|
background: "radial-gradient(circle, rgba(220, 38, 38, 0.72) 0%, rgba(220, 38, 38, 0.2) 58%, transparent 70%)",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return styles;
|
||||||
|
}, [board, isGameOver, isMyTurn, queuedMove, selectedSquare, selectedTargets, view.isCheck, view.moveHistory, view.turn]);
|
||||||
|
|
||||||
|
const queueMove = useCallback((move: MoveIntent) => {
|
||||||
|
setQueuedMove(move);
|
||||||
|
setSelectedSquare(null);
|
||||||
|
setLocalNotice({ tone: "info", text: `Premove armed: ${formatMoveIntent(move)}` });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const chooseOrPlayMove = useCallback((from: Square, to: Square, mode: "move" | "premove") => {
|
||||||
|
const piece = board.get(from);
|
||||||
|
const promotionRequired = isPromotionMove(piece?.type, piece?.color, to);
|
||||||
|
const moveIntent: MoveIntent = { from, to, mode };
|
||||||
|
|
||||||
|
if (promotionRequired) {
|
||||||
|
setPendingPromotion(moveIntent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === "move") {
|
||||||
|
onAction({ type: "move", from, to });
|
||||||
|
setSelectedSquare(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queueMove(moveIntent);
|
||||||
|
}, [board, onAction, queueMove]);
|
||||||
|
|
||||||
|
const onSquareClick = useCallback((square: Square) => {
|
||||||
|
if (!playerView || isSpectator || isGameOver) return;
|
||||||
|
|
||||||
|
const clickedPiece = board.get(square);
|
||||||
|
const ownColorCode = myColor === "white" ? "w" : "b";
|
||||||
|
const isOwnPiece = clickedPiece?.color === ownColorCode;
|
||||||
|
const mode: "move" | "premove" = isMyTurn ? "move" : "premove";
|
||||||
|
|
||||||
|
if (selectedSquare && selectedTargets.includes(square)) {
|
||||||
|
chooseOrPlayMove(selectedSquare, square, mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedSquare === square) {
|
||||||
|
setSelectedSquare(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOwnPiece) {
|
||||||
|
setSelectedSquare(square);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedSquare(null);
|
||||||
|
}, [board, chooseOrPlayMove, isGameOver, isMyTurn, isSpectator, myColor, playerView, selectedSquare, selectedTargets]);
|
||||||
|
|
||||||
|
const onPieceDrop = useCallback(({ sourceSquare, targetSquare }: { piece: unknown; sourceSquare: string; targetSquare: string | null }): boolean => {
|
||||||
|
if (!playerView || isSpectator || isGameOver || !targetSquare) return false;
|
||||||
|
|
||||||
|
const from = sourceSquare as Square;
|
||||||
|
const to = targetSquare as Square;
|
||||||
|
|
||||||
|
const isAllowed = isMyTurn
|
||||||
|
? playerView.legalMoves.some(move => move.from === from && move.to === to)
|
||||||
|
: getPremoveTargets(board, from, myColor).includes(to);
|
||||||
|
|
||||||
|
if (!isAllowed) return false;
|
||||||
|
|
||||||
|
chooseOrPlayMove(from, to, isMyTurn ? "move" : "premove");
|
||||||
|
return isMyTurn ? true : false;
|
||||||
|
}, [board, chooseOrPlayMove, isGameOver, isMyTurn, isSpectator, myColor, playerView]);
|
||||||
|
|
||||||
|
const handlePromotion = useCallback((promotion: string) => {
|
||||||
|
if (!pendingPromotion) return;
|
||||||
|
|
||||||
|
if (pendingPromotion.mode === "move") {
|
||||||
|
onAction({
|
||||||
|
type: "move",
|
||||||
|
from: pendingPromotion.from,
|
||||||
|
to: pendingPromotion.to,
|
||||||
|
promotion,
|
||||||
|
});
|
||||||
|
setSelectedSquare(null);
|
||||||
|
} else {
|
||||||
|
queueMove({ ...pendingPromotion, promotion });
|
||||||
|
}
|
||||||
|
|
||||||
|
setPendingPromotion(null);
|
||||||
|
}, [onAction, pendingPromotion, queueMove]);
|
||||||
|
|
||||||
|
const canDragPiece = useCallback(({ piece }: { isSparePiece: boolean; piece: { pieceType: string }; square: string | null }): boolean => {
|
||||||
|
if (isSpectator || isGameOver || !playerView) return false;
|
||||||
|
const pieceColor = piece.pieceType[0] === "w" ? "white" : "black";
|
||||||
|
return pieceColor === myColor;
|
||||||
|
}, [isSpectator, isGameOver, playerView, myColor]);
|
||||||
|
|
||||||
|
const getPlayerName = useCallback((color: "white" | "black") => {
|
||||||
|
if (isPlayerView(state)) {
|
||||||
|
if (isSoloMode) return players[0]?.username ?? colorLabel(color);
|
||||||
|
|
||||||
|
const selfId = color === state.myColor ? myPlayerId : players.find(player => player.discordId !== myPlayerId)?.discordId;
|
||||||
|
return players.find(player => player.discordId === selfId)?.username ?? (color === myColor ? "You" : "Opponent");
|
||||||
|
}
|
||||||
|
|
||||||
|
const spectatorState = state as SpectatorView;
|
||||||
|
return players.find(player => player.discordId === spectatorState.players[color])?.username ?? colorLabel(color);
|
||||||
|
}, [isSoloMode, myColor, myPlayerId, players, state]);
|
||||||
|
|
||||||
|
const topColor: "white" | "black" = boardOrientation === "white" ? "black" : "white";
|
||||||
|
const bottomColor: "white" | "black" = boardOrientation;
|
||||||
|
|
||||||
|
const showDrawOffer = playerView && view.drawOffer && view.drawOffer !== playerView.myColor && !isGameOver;
|
||||||
|
const showDrawButton = playerView && !view.drawOffer && !isGameOver;
|
||||||
|
const pendingDrawOffer = playerView && view.drawOffer === playerView.myColor;
|
||||||
|
const latestMove = view.moveHistory.length > 0 ? view.moveHistory[view.moveHistory.length - 1] : null;
|
||||||
|
const moveNumber = Math.floor(view.moveHistory.length / 2) + 1;
|
||||||
|
|
||||||
|
const timeControlLabel = typeof roomOptions?.timeControl === "string"
|
||||||
|
? CHESS_TIME_CONTROL_LABELS[roomOptions.timeControl] ?? roomOptions.timeControl
|
||||||
|
: liveClock
|
||||||
|
? "Clocked game"
|
||||||
|
: "No clock";
|
||||||
|
|
||||||
|
const statusTone = isGameOver
|
||||||
|
? view.result === "draw" ? "warning" : "primary"
|
||||||
|
: showDrawOffer ? "info"
|
||||||
|
: isMyTurn ? "success" : "default";
|
||||||
|
|
||||||
|
const statusCardClass = statusTone === "warning"
|
||||||
|
? "border-warning/30 bg-warning/10"
|
||||||
|
: statusTone === "primary"
|
||||||
|
? "border-primary/30 bg-primary/10"
|
||||||
|
: statusTone === "info"
|
||||||
|
? "border-info/30 bg-info/10"
|
||||||
|
: statusTone === "success"
|
||||||
|
? "border-success/30 bg-success/10"
|
||||||
|
: "border-border/60 bg-card/80";
|
||||||
|
|
||||||
|
const statusTitle = isGameOver
|
||||||
|
? view.result === "draw"
|
||||||
|
? "Drawn game"
|
||||||
|
: `${getPlayerName(view.result!)} wins`
|
||||||
|
: showDrawOffer
|
||||||
|
? "Draw offered"
|
||||||
|
: isSpectator
|
||||||
|
? `${colorLabel(view.turn)} to move`
|
||||||
|
: isMyTurn
|
||||||
|
? "Your move"
|
||||||
|
: queuedMove
|
||||||
|
? "Premove ready"
|
||||||
|
: "Waiting for opponent";
|
||||||
|
|
||||||
|
const statusText = isGameOver
|
||||||
|
? view.resultReason ?? "Game over"
|
||||||
|
: showDrawOffer
|
||||||
|
? `${getPlayerName(view.drawOffer!)} offered a draw.`
|
||||||
|
: isSpectator
|
||||||
|
? view.isCheck
|
||||||
|
? `${getPlayerName(view.turn)} is in check.`
|
||||||
|
: `Watching ${getPlayerName("white")} vs ${getPlayerName("black")}.`
|
||||||
|
: isMyTurn
|
||||||
|
? view.isCheck
|
||||||
|
? "Your king is in check. Respond now."
|
||||||
|
: `Play as ${colorLabel(myColor)}.`
|
||||||
|
: queuedMove
|
||||||
|
? `${formatMoveIntent(queuedMove)} will fire automatically if legal.`
|
||||||
|
: `${getPlayerName(view.turn)} is thinking.`;
|
||||||
|
|
||||||
|
const metaFacts = [
|
||||||
|
{ label: isSpectator ? "View" : "Side", value: isSpectator ? "Spectator" : colorLabel(myColor) },
|
||||||
|
{ label: "Time", value: timeControlLabel },
|
||||||
|
{ label: "Move", value: `${moveNumber}` },
|
||||||
|
{ label: "Latest", value: latestMove?.san ?? "Opening" },
|
||||||
|
];
|
||||||
|
|
||||||
|
if ((roomOptions?.betAmount ?? 0) > 0) {
|
||||||
|
metaFacts.push({ label: "Wager", value: `${roomOptions!.betAmount} AU` });
|
||||||
|
}
|
||||||
|
|
||||||
|
const recapFacts = [
|
||||||
|
{ label: "Turn", value: colorLabel(view.turn) },
|
||||||
|
{ label: "Latest", value: latestMove?.san ?? "Opening" },
|
||||||
|
{ label: "Clock", value: timeControlLabel },
|
||||||
|
];
|
||||||
|
|
||||||
|
if ((roomOptions?.betAmount ?? 0) > 0) {
|
||||||
|
recapFacts.push({ label: "Stake", value: `${roomOptions!.betAmount} AU` });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto w-full max-w-[1440px]">
|
||||||
|
<div className="mb-5 rounded-[28px] border border-border/60 bg-[radial-gradient(circle_at_top,rgba(233,195,73,0.12),transparent_42%),linear-gradient(180deg,rgba(255,255,255,0.03),rgba(255,255,255,0.01))] p-4 shadow-[0_18px_50px_rgba(0,0,0,0.22)]">
|
||||||
|
<div className="flex flex-wrap items-start justify-between gap-4">
|
||||||
|
<div className="min-w-0">
|
||||||
|
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-text-disabled">Quick Recap</div>
|
||||||
|
<div className="mt-2 text-xl font-display font-semibold text-foreground">{statusTitle}</div>
|
||||||
|
<div className="mt-1 text-sm text-text-secondary">{statusText}</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-2 text-xs">
|
||||||
|
{view.isCheck && !isGameOver && (
|
||||||
|
<span className="rounded-full bg-destructive/15 px-2.5 py-1 text-destructive">Check</span>
|
||||||
|
)}
|
||||||
|
{!isSpectator && (
|
||||||
|
<span className="rounded-full bg-card/70 px-2.5 py-1 text-text-secondary">
|
||||||
|
Playing as {colorLabel(myColor)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{isSpectator && (
|
||||||
|
<span className="inline-flex items-center gap-1 rounded-full bg-card/70 px-2.5 py-1 text-text-secondary">
|
||||||
|
<Eye className="h-3.5 w-3.5" />
|
||||||
|
Spectating
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{queuedMove && !isGameOver && (
|
||||||
|
<span className="rounded-full bg-info/15 px-2.5 py-1 text-info">
|
||||||
|
Premove: {formatMoveIntent(queuedMove)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4 grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
|
||||||
|
{recapFacts.map(fact => (
|
||||||
|
<div key={fact.label} className="rounded-2xl border border-border/60 bg-card/80 px-4 py-3">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.18em] text-text-disabled">{fact.label}</div>
|
||||||
|
<div className="mt-1 truncate text-sm font-semibold text-foreground">{fact.value}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-5 xl:grid-cols-[minmax(0,1fr)_360px] xl:items-start">
|
||||||
|
<section className="min-w-0">
|
||||||
|
<div className="mx-auto flex max-w-[860px] flex-col items-center gap-4 lg:min-h-[calc(100vh-14rem)] lg:justify-center">
|
||||||
|
<div className="w-full rounded-[28px] border border-border/60 bg-[radial-gradient(circle_at_top,rgba(233,195,73,0.16),transparent_42%),linear-gradient(180deg,rgba(255,255,255,0.02),rgba(255,255,255,0))] p-3 shadow-[0_28px_80px_rgba(0,0,0,0.3)] sm:p-4 md:p-6">
|
||||||
|
<PlayerCard
|
||||||
|
name={getPlayerName(topColor)}
|
||||||
|
color={topColor}
|
||||||
|
isTurn={view.turn === topColor && !isGameOver}
|
||||||
|
isSelf={!isSpectator && topColor === myColor}
|
||||||
|
time={liveClock ? liveClock[topColor] : null}
|
||||||
|
clockActive={liveClock?.activeColor === topColor}
|
||||||
|
clockLow={!!liveClock && liveClock[topColor] < 30_000 && liveClock.activeColor === topColor}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="my-4 flex justify-center">
|
||||||
|
<div
|
||||||
|
className="relative aspect-square w-full max-w-[720px]"
|
||||||
|
style={{ width: "min(100%, 78vh, 720px)" }}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0 overflow-hidden rounded-[24px] shadow-[0_24px_60px_rgba(0,0,0,0.45)]">
|
||||||
|
<Chessboard options={{
|
||||||
|
position: view.fen,
|
||||||
|
boardOrientation,
|
||||||
|
pieces: chessPieces,
|
||||||
|
onPieceDrop,
|
||||||
|
onSquareClick: ({ square }: { piece: unknown; square: string }) => onSquareClick(square as Square),
|
||||||
|
canDragPiece,
|
||||||
|
squareStyles: customSquareStyles,
|
||||||
|
showNotation: true,
|
||||||
|
darkSquareStyle: { backgroundColor: "#7c6554" },
|
||||||
|
lightSquareStyle: { backgroundColor: "#eadfcb" },
|
||||||
|
boardStyle: { borderRadius: "24px" },
|
||||||
|
dropSquareStyle: { boxShadow: "inset 0 0 0 4px rgba(233, 195, 73, 0.65)" },
|
||||||
|
animationDurationInMs: 180,
|
||||||
|
allowDragging: !isSpectator && !isGameOver,
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{pendingPromotion && (
|
||||||
|
<div className="absolute inset-0 z-10 flex items-center justify-center rounded-[24px] bg-black/55 p-4">
|
||||||
|
<div className="w-full max-w-sm rounded-2xl border border-border/60 bg-surface-container-highest p-4 shadow-[0_24px_48px_rgba(0,0,0,0.45)]">
|
||||||
|
<div className="text-center text-[11px] font-semibold uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
Promotion
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 text-center text-sm text-text-secondary">
|
||||||
|
Choose the piece for {formatMoveIntent(pendingPromotion)}
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 grid grid-cols-4 gap-2">
|
||||||
|
{(["q", "r", "b", "n"] as const).map(piece => {
|
||||||
|
const key = `${myColor === "white" ? "w" : "b"}${piece.toUpperCase()}`;
|
||||||
|
const PieceSvg = chessPieces[key];
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={piece}
|
||||||
|
onClick={() => handlePromotion(piece)}
|
||||||
|
className="flex h-16 items-center justify-center rounded-xl border border-border/60 bg-raised p-2 transition-colors hover:bg-primary/15"
|
||||||
|
>
|
||||||
|
{PieceSvg && <PieceSvg />}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setPendingPromotion(null)}
|
||||||
|
className="mt-3 w-full rounded-xl px-3 py-2 text-sm text-text-tertiary transition-colors hover:bg-card hover:text-foreground"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<PlayerCard
|
||||||
|
name={getPlayerName(bottomColor)}
|
||||||
|
color={bottomColor}
|
||||||
|
isTurn={view.turn === bottomColor && !isGameOver}
|
||||||
|
isSelf={!isSpectator && bottomColor === myColor}
|
||||||
|
time={liveClock ? liveClock[bottomColor] : null}
|
||||||
|
clockActive={liveClock?.activeColor === bottomColor}
|
||||||
|
clockLow={!!liveClock && liveClock[bottomColor] < 30_000 && liveClock.activeColor === bottomColor}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="mt-4 flex flex-wrap items-center justify-center gap-2">
|
||||||
|
{!isSpectator && (
|
||||||
|
<button
|
||||||
|
onClick={() => setQueuedMove(null)}
|
||||||
|
disabled={!queuedMove}
|
||||||
|
className={`inline-flex items-center gap-2 rounded-xl px-3 py-2 text-sm transition-colors ${
|
||||||
|
queuedMove
|
||||||
|
? "bg-info/10 text-info hover:bg-info/15"
|
||||||
|
: "bg-raised text-text-disabled"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<TimerReset className="h-4 w-4" />
|
||||||
|
Clear premove
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => setBoardOrientation(current => current === "white" ? "black" : "white")}
|
||||||
|
className="inline-flex items-center gap-2 rounded-xl bg-raised px-3 py-2 text-sm text-text-secondary transition-colors hover:text-foreground"
|
||||||
|
>
|
||||||
|
<RotateCw className="h-4 w-4" />
|
||||||
|
Flip board
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{liveClock && liveClock.increment > 0 && (
|
||||||
|
<div className="rounded-xl bg-card px-3 py-2 text-xs text-text-tertiary">
|
||||||
|
+{liveClock.increment / 1000}s increment
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSpectator && !isMyTurn && !queuedMove && !isGameOver && (
|
||||||
|
<div className="rounded-xl bg-card px-3 py-2 text-xs text-text-tertiary">
|
||||||
|
You can queue a premove while waiting.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<aside className="w-full xl:sticky xl:top-6">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className={`rounded-2xl border p-4 ${statusCardClass}`}>
|
||||||
|
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
Match State
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-xl font-display font-semibold text-foreground">{statusTitle}</div>
|
||||||
|
<div className="mt-1 text-sm text-text-secondary">{statusText}</div>
|
||||||
|
<div className="mt-4 flex flex-wrap gap-2 text-xs">
|
||||||
|
<span className="rounded-full bg-card/70 px-2.5 py-1 text-text-secondary">
|
||||||
|
{colorLabel(view.turn)} to move
|
||||||
|
</span>
|
||||||
|
{view.isCheck && !isGameOver && (
|
||||||
|
<span className="rounded-full bg-destructive/15 px-2.5 py-1 text-destructive">Check</span>
|
||||||
|
)}
|
||||||
|
{!isSpectator && (
|
||||||
|
<span className="rounded-full bg-card/70 px-2.5 py-1 text-text-secondary">
|
||||||
|
Playing as {colorLabel(myColor)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{isSpectator && (
|
||||||
|
<span className="inline-flex items-center gap-1 rounded-full bg-card/70 px-2.5 py-1 text-text-secondary">
|
||||||
|
<Eye className="h-3.5 w-3.5" />
|
||||||
|
Spectating
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{queuedMove && !isGameOver && (
|
||||||
|
<span className="rounded-full bg-info/15 px-2.5 py-1 text-info">
|
||||||
|
Premove: {formatMoveIntent(queuedMove)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
{metaFacts.map(fact => (
|
||||||
|
<div key={fact.label} className="rounded-2xl border border-border/60 bg-card/80 p-3">
|
||||||
|
<div className="text-[11px] uppercase tracking-[0.18em] text-text-disabled">{fact.label}</div>
|
||||||
|
<div className="mt-1 truncate text-sm font-semibold text-foreground">{fact.value}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{localNotice && (
|
||||||
|
<div className={`rounded-2xl border px-4 py-3 text-sm ${
|
||||||
|
localNotice.tone === "warning"
|
||||||
|
? "border-warning/30 bg-warning/10 text-warning"
|
||||||
|
: localNotice.tone === "success"
|
||||||
|
? "border-success/30 bg-success/10 text-success"
|
||||||
|
: "border-info/30 bg-info/10 text-info"
|
||||||
|
}`}>
|
||||||
|
{localNotice.text}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showDrawOffer && (
|
||||||
|
<div className="rounded-2xl border border-info/30 bg-info/10 p-4">
|
||||||
|
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
Decision Needed
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 text-sm text-info">
|
||||||
|
{getPlayerName(view.drawOffer!)} offered a draw.
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 flex gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => onAction({ type: "accept_draw" })}
|
||||||
|
className="flex-1 inline-flex items-center justify-center gap-1.5 rounded-xl bg-success/15 px-3 py-2 text-sm font-semibold text-success transition-colors hover:bg-success/25"
|
||||||
|
>
|
||||||
|
<Check className="h-4 w-4" />
|
||||||
|
Accept
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => onAction({ type: "decline_draw" })}
|
||||||
|
className="flex-1 inline-flex items-center justify-center gap-1.5 rounded-xl bg-destructive/15 px-3 py-2 text-sm font-semibold text-destructive transition-colors hover:bg-destructive/25"
|
||||||
|
>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
Decline
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{pendingDrawOffer && (
|
||||||
|
<div className="rounded-2xl border border-info/30 bg-info/10 px-4 py-3 text-sm text-info">
|
||||||
|
Draw offer sent. Waiting for a response.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="rounded-2xl border border-border/60 bg-card/80 p-4">
|
||||||
|
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
Controls
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 grid grid-cols-2 gap-2">
|
||||||
|
{!isSpectator && !isGameOver && (
|
||||||
|
<button
|
||||||
|
onClick={() => onAction({ type: "resign" })}
|
||||||
|
className="inline-flex items-center justify-center gap-2 rounded-xl bg-raised px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-destructive/10 hover:text-destructive"
|
||||||
|
>
|
||||||
|
<Flag className="h-4 w-4" />
|
||||||
|
Resign
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isSpectator && !isGameOver && showDrawButton && (
|
||||||
|
<button
|
||||||
|
onClick={() => onAction({ type: "offer_draw" })}
|
||||||
|
className="inline-flex items-center justify-center gap-2 rounded-xl bg-raised px-3 py-2 text-sm text-text-secondary transition-colors hover:bg-info/10 hover:text-info"
|
||||||
|
>
|
||||||
|
<Handshake className="h-4 w-4" />
|
||||||
|
Offer draw
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => setBoardOrientation(current => current === "white" ? "black" : "white")}
|
||||||
|
className="inline-flex items-center justify-center gap-2 rounded-xl bg-raised px-3 py-2 text-sm text-text-secondary transition-colors hover:text-foreground"
|
||||||
|
>
|
||||||
|
<RotateCw className="h-4 w-4" />
|
||||||
|
Flip
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{!isSpectator && (
|
||||||
|
<button
|
||||||
|
onClick={() => setQueuedMove(null)}
|
||||||
|
className="inline-flex items-center justify-center gap-2 rounded-xl bg-raised px-3 py-2 text-sm text-text-secondary transition-colors hover:text-foreground"
|
||||||
|
>
|
||||||
|
<TimerReset className="h-4 w-4" />
|
||||||
|
Reset premove
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!isSpectator && !isGameOver && (
|
||||||
|
<div className="mt-3 text-xs text-text-disabled">
|
||||||
|
Drag or click pieces. Off-turn interactions queue a premove and play automatically if the move is still legal.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-hidden rounded-2xl border border-border/60 bg-card/80">
|
||||||
|
<div className="border-b border-border/60 px-4 py-3">
|
||||||
|
<div className="text-[11px] font-semibold uppercase tracking-[0.18em] text-text-disabled">
|
||||||
|
Move History
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 text-sm text-text-secondary">
|
||||||
|
{latestMove ? `Latest: ${latestMove.san}` : "Opening phase"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref={moveHistoryRef} className="max-h-[360px] overflow-y-auto px-4 py-3">
|
||||||
|
<MoveHistory moves={view.moveHistory} containerRef={moveHistoryRef} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
151
panel/src/games/chess/pieces.tsx
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
/**
|
||||||
|
* Inline SVG chess pieces (cburnett set from Lichess, CC BY-SA).
|
||||||
|
*
|
||||||
|
* These are rendered as native <svg> JSX — not <img> tags — so they live in
|
||||||
|
* the same React/DOM tree as react-chessboard's Piece wrapper and don't
|
||||||
|
* interfere with dnd-kit's pointer-event pipeline.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { PieceRenderObject } from "react-chessboard";
|
||||||
|
|
||||||
|
type PieceProps = { svgStyle?: React.CSSProperties };
|
||||||
|
|
||||||
|
function svg(children: React.ReactNode) {
|
||||||
|
return (props?: PieceProps) => (
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 45 45"
|
||||||
|
width="100%" height="100%" style={props?.svgStyle}>
|
||||||
|
{children}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const chessPieces: PieceRenderObject = {
|
||||||
|
// ── White ──
|
||||||
|
|
||||||
|
wP: svg(
|
||||||
|
<path fill="#fff" stroke="#000" strokeLinecap="round" strokeWidth="1.5"
|
||||||
|
d="M22.5 9c-2.21 0-4 1.79-4 4 0 .89.29 1.71.78 2.38C17.33 16.5 16 18.59 16 21c0 2.03.94 3.84 2.41 5.03-3 1.06-7.41 5.55-7.41 13.47h23c0-7.92-4.41-12.41-7.41-13.47 1.47-1.19 2.41-3 2.41-5.03 0-2.41-1.33-4.5-3.28-5.62.49-.67.78-1.49.78-2.38 0-2.21-1.79-4-4-4z" />
|
||||||
|
),
|
||||||
|
|
||||||
|
wR: svg(
|
||||||
|
<g fill="#fff" fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<path strokeLinecap="butt" d="M9 39h27v-3H9zm3-3v-4h21v4zm-1-22V9h4v2h5V9h5v2h5V9h4v5" />
|
||||||
|
<path d="m34 14-3 3H14l-3-3" />
|
||||||
|
<path strokeLinecap="butt" strokeLinejoin="miter" d="M31 17v12.5H14V17" />
|
||||||
|
<path d="m31 29.5 1.5 2.5h-20l1.5-2.5" />
|
||||||
|
<path fill="none" strokeLinejoin="miter" d="M11 14h23" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
|
||||||
|
wN: svg(
|
||||||
|
<g fill="none" fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<path fill="#fff" d="M22 10c10.5 1 16.5 8 16 29H15c0-9 10-6.5 8-21" />
|
||||||
|
<path fill="#fff" d="M24 18c.38 2.91-5.55 7.37-8 9-3 2-2.82 4.34-5 4-1.042-.94 1.41-3.04 0-3-1 0 .19 1.23-1 2-1 0-4.003 1-4-4 0-2 6-12 6-12s1.89-1.9 2-3.5c-.73-.994-.5-2-.5-3 1-1 3 2.5 3 2.5h2s.78-1.992 2.5-3c1 0 1 3 1 3" />
|
||||||
|
<path fill="#000" d="M9.5 25.5a.5.5 0 1 1-1 0 .5.5 0 1 1 1 0m5.433-9.75a.5 1.5 30 1 1-.866-.5.5 1.5 30 1 1 .866.5" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
|
||||||
|
wB: svg(
|
||||||
|
<g fill="none" fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<g fill="#fff" strokeLinecap="butt">
|
||||||
|
<path d="M9 36c3.39-.97 10.11.43 13.5-2 3.39 2.43 10.11 1.03 13.5 2 0 0 1.65.54 3 2-.68.97-1.65.99-3 .5-3.39-.97-10.11.46-13.5-1-3.39 1.46-10.11.03-13.5 1-1.35.49-2.32.47-3-.5 1.35-1.94 3-2 3-2z" />
|
||||||
|
<path d="M15 32c2.5 2.5 12.5 2.5 15 0 .5-1.5 0-2 0-2 0-2.5-2.5-4-2.5-4 5.5-1.5 6-11.5-5-15.5-11 4-10.5 14-5 15.5 0 0-2.5 1.5-2.5 4 0 0-.5.5 0 2z" />
|
||||||
|
<path d="M25 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 1 1 5 0z" />
|
||||||
|
</g>
|
||||||
|
<path strokeLinejoin="miter" d="M17.5 26h10M15 30h15m-7.5-14.5v5M20 18h5" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
|
||||||
|
wQ: svg(
|
||||||
|
<g fill="#fff" fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<path d="M8 12a2 2 0 1 1-4 0 2 2 0 1 1 4 0m16.5-4.5a2 2 0 1 1-4 0 2 2 0 1 1 4 0M41 12a2 2 0 1 1-4 0 2 2 0 1 1 4 0M16 8.5a2 2 0 1 1-4 0 2 2 0 1 1 4 0M33 9a2 2 0 1 1-4 0 2 2 0 1 1 4 0" />
|
||||||
|
<path strokeLinecap="butt" d="M9 26c8.5-1.5 21-1.5 27 0l2-12-7 11V11l-5.5 13.5-3-15-3 15-5.5-14V25L7 14z" />
|
||||||
|
<path strokeLinecap="butt" d="M9 26c0 2 1.5 2 2.5 4 1 1.5 1 1 .5 3.5-1.5 1-1.5 2.5-1.5 2.5-1.5 1.5.5 2.5.5 2.5 6.5 1 16.5 1 23 0 0 0 1.5-1 0-2.5 0 0 .5-1.5-1-2.5-.5-2.5-.5-2 .5-3.5 1-2 2.5-2 2.5-4-8.5-1.5-18.5-1.5-27 0z" />
|
||||||
|
<path fill="none" d="M11.5 30c3.5-1 18.5-1 22 0M12 33.5c6-1 15-1 21 0" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
|
||||||
|
wK: svg(
|
||||||
|
<g fill="none" fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<path strokeLinejoin="miter" d="M22.5 11.63V6M20 8h5" />
|
||||||
|
<path fill="#fff" strokeLinecap="butt" strokeLinejoin="miter"
|
||||||
|
d="M22.5 25s4.5-7.5 3-10.5c0 0-1-2.5-3-2.5s-3 2.5-3 2.5c-1.5 3 3 10.5 3 10.5" />
|
||||||
|
<path fill="#fff"
|
||||||
|
d="M11.5 37c5.5 3.5 15.5 3.5 21 0v-7s9-4.5 6-10.5c-4-6.5-13.5-3.5-16 4V27v-3.5c-3.5-7.5-13-10.5-16-4-3 6 5 10 5 10z" />
|
||||||
|
<path d="M11.5 30c5.5-3 15.5-3 21 0m-21 3.5c5.5-3 15.5-3 21 0m-21 3.5c5.5-3 15.5-3 21 0" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
|
||||||
|
// ── Black ──
|
||||||
|
|
||||||
|
bP: svg(
|
||||||
|
<path stroke="#000" strokeLinecap="round" strokeWidth="1.5"
|
||||||
|
d="M22.5 9a4 4 0 0 0-3.22 6.38 6.48 6.48 0 0 0-.87 10.65c-3 1.06-7.41 5.55-7.41 13.47h23c0-7.92-4.41-12.41-7.41-13.47a6.46 6.46 0 0 0-.87-10.65A4.01 4.01 0 0 0 22.5 9z" />
|
||||||
|
),
|
||||||
|
|
||||||
|
bR: svg(
|
||||||
|
<g fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<path strokeLinecap="butt" d="M9 39h27v-3H9zm3.5-7 1.5-2.5h17l1.5 2.5zm-.5 4v-4h21v4z" />
|
||||||
|
<path strokeLinecap="butt" strokeLinejoin="miter" d="M14 29.5v-13h17v13z" />
|
||||||
|
<path strokeLinecap="butt" d="M14 16.5 11 14h23l-3 2.5zM11 14V9h4v2h5V9h5v2h5V9h4v5z" />
|
||||||
|
<path fill="none" stroke="#ececec" strokeLinejoin="miter" strokeWidth="1"
|
||||||
|
d="M12 35.5h21m-20-4h19m-18-2h17m-17-13h17M11 14h23" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
|
||||||
|
bN: svg(
|
||||||
|
<g fill="none" fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<path fill="#000" d="M22 10c10.5 1 16.5 8 16 29H15c0-9 10-6.5 8-21" />
|
||||||
|
<path fill="#000" d="M24 18c.38 2.91-5.55 7.37-8 9-3 2-2.82 4.34-5 4-1.04-.94 1.41-3.04 0-3-1 0 .19 1.23-1 2-1 0-4 1-4-4 0-2 6-12 6-12s1.89-1.9 2-3.5c-.73-1-.5-2-.5-3 1-1 3 2.5 3 2.5h2s.78-2 2.5-3c1 0 1 3 1 3" />
|
||||||
|
<path fill="#ececec" stroke="#ececec"
|
||||||
|
d="M9.5 25.5a.5.5 0 1 1-1 0 .5.5 0 1 1 1 0m5.43-9.75a.5 1.5 30 1 1-.86-.5.5 1.5 30 1 1 .86.5" />
|
||||||
|
<path fill="#ececec" stroke="none"
|
||||||
|
d="m24.55 10.4-.45 1.45.5.15c3.15 1 5.65 2.49 7.9 6.75S35.75 29.06 35.25 39l-.05.5h2.25l.05-.5c.5-10.06-.88-16.85-3.25-21.34s-5.79-6.64-9.19-7.16z" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
|
||||||
|
bB: svg(
|
||||||
|
<g fill="none" fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<g fill="#000" strokeLinecap="butt">
|
||||||
|
<path d="M9 36c3.4-1 10.1.4 13.5-2 3.4 2.4 10.1 1 13.5 2 0 0 1.6.5 3 2-.7 1-1.6 1-3 .5-3.4-1-10.1.5-13.5-1-3.4 1.5-10.1 0-13.5 1-1.4.5-2.3.5-3-.5 1.4-2 3-2 3-2z" />
|
||||||
|
<path d="M15 32c2.5 2.5 12.5 2.5 15 0 .5-1.5 0-2 0-2 0-2.5-2.5-4-2.5-4 5.5-1.5 6-11.5-5-15.5-11 4-10.5 14-5 15.5 0 0-2.5 1.5-2.5 4 0 0-.5.5 0 2z" />
|
||||||
|
<path d="M25 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 1 1 5 0z" />
|
||||||
|
</g>
|
||||||
|
<path stroke="#ececec" strokeLinejoin="miter" d="M17.5 26h10M15 30h15m-7.5-14.5v5M20 18h5" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
|
||||||
|
bQ: svg(
|
||||||
|
<g fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<g stroke="none">
|
||||||
|
<circle cx="6" cy="12" r="2.75" />
|
||||||
|
<circle cx="14" cy="9" r="2.75" />
|
||||||
|
<circle cx="22.5" cy="8" r="2.75" />
|
||||||
|
<circle cx="31" cy="9" r="2.75" />
|
||||||
|
<circle cx="39" cy="12" r="2.75" />
|
||||||
|
</g>
|
||||||
|
<path strokeLinecap="butt"
|
||||||
|
d="M9 26c8.5-1.5 21-1.5 27 0l2.5-12.5L31 25l-.3-14.1-5.2 13.6-3-14.5-3 14.5-5.2-13.6L14 25 6.5 13.5z" />
|
||||||
|
<path strokeLinecap="butt"
|
||||||
|
d="M9 26c0 2 1.5 2 2.5 4 1 1.5 1 1 .5 3.5-1.5 1-1.5 2.5-1.5 2.5-1.5 1.5.5 2.5.5 2.5 6.5 1 16.5 1 23 0 0 0 1.5-1 0-2.5 0 0 .5-1.5-1-2.5-.5-2.5-.5-2 .5-3.5 1-2 2.5-2 2.5-4-8.5-1.5-18.5-1.5-27 0z" />
|
||||||
|
<path fill="none" strokeLinecap="butt" d="M11 38.5a35 35 1 0 0 23 0" />
|
||||||
|
<path fill="none" stroke="#ececec"
|
||||||
|
d="M11 29a35 35 1 0 1 23 0m-21.5 2.5h20m-21 3a35 35 1 0 0 22 0m-23 3a35 35 1 0 0 24 0" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
|
||||||
|
bK: svg(
|
||||||
|
<g fill="none" fillRule="evenodd" stroke="#000" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.5">
|
||||||
|
<path strokeLinejoin="miter" d="M22.5 11.6V6" />
|
||||||
|
<path fill="#000" strokeLinecap="butt" strokeLinejoin="miter"
|
||||||
|
d="M22.5 25s4.5-7.5 3-10.5c0 0-1-2.5-3-2.5s-3 2.5-3 2.5c-1.5 3 3 10.5 3 10.5" />
|
||||||
|
<path fill="#000"
|
||||||
|
d="M11.5 37a22.3 22.3 0 0 0 21 0v-7s9-4.5 6-10.5c-4-6.5-13.5-3.5-16 4V27v-3.5c-3.5-7.5-13-10.5-16-4-3 6 5 10 5 10z" />
|
||||||
|
<path strokeLinejoin="miter" d="M20 8h5" />
|
||||||
|
<path stroke="#ececec"
|
||||||
|
d="M32 29.5s8.5-4 6-9.7C34.1 14 25 18 22.5 24.6v2.1-2.1C20 18 9.9 14 7 19.9c-2.5 5.6 4.8 9 4.8 9" />
|
||||||
|
<path stroke="#ececec"
|
||||||
|
d="M11.5 30c5.5-3 15.5-3 21 0m-21 3.5c5.5-3 15.5-3 21 0m-21 3.5c5.5-3 15.5-3 21 0" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
|
};
|
||||||
25
panel/src/games/chess/timeControls.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
export interface ChessTimeControlOption {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
category: "Bullet" | "Blitz" | "Rapid" | "Classical" | "No Clock";
|
||||||
|
detail: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CHESS_TIME_CONTROLS: ChessTimeControlOption[] = [
|
||||||
|
{ key: "bullet_1_0", label: "1+0", category: "Bullet", detail: "Instant games with no increment." },
|
||||||
|
{ key: "bullet_2_1", label: "2+1", category: "Bullet", detail: "Fast clock with a small recovery buffer." },
|
||||||
|
{ key: "blitz_3_0", label: "3+0", category: "Blitz", detail: "Classic blitz pace for sharp openings." },
|
||||||
|
{ key: "blitz_3_2", label: "3+2", category: "Blitz", detail: "Short games with enough time to convert." },
|
||||||
|
{ key: "blitz_5_0", label: "5+0", category: "Blitz", detail: "Simple blitz with no increment pressure." },
|
||||||
|
{ key: "blitz_5_3", label: "5+3", category: "Blitz", detail: "Balanced default for most quick matches." },
|
||||||
|
{ key: "rapid_10_0", label: "10+0", category: "Rapid", detail: "More room for calculated middlegames." },
|
||||||
|
{ key: "rapid_15_10", label: "15+10", category: "Rapid", detail: "Longer sessions with generous increment." },
|
||||||
|
{ key: "classical_30_0", label: "30+0", category: "Classical", detail: "Deliberate play for deep analysis." },
|
||||||
|
{ key: "none", label: "None", category: "No Clock", detail: "Untimed board for casual or teaching games." },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const CHESS_TIME_CONTROL_LABELS = Object.fromEntries(
|
||||||
|
CHESS_TIME_CONTROLS.map(control => [control.key, `${control.category === "No Clock" ? "" : `${control.category} `}${control.label}`.trim()]),
|
||||||
|
) as Record<string, string>;
|
||||||
|
|
||||||
|
export const CHESS_TIME_CONTROL_CATEGORIES = ["Bullet", "Blitz", "Rapid", "Classical", "No Clock"] as const;
|
||||||
@@ -6,13 +6,20 @@ export interface GameUIProps {
|
|||||||
isSpectator: boolean;
|
isSpectator: boolean;
|
||||||
onAction: (action: unknown) => void;
|
onAction: (action: unknown) => void;
|
||||||
players: { discordId: string; username: string }[];
|
players: { discordId: string; username: string }[];
|
||||||
|
roundResult?: { settlements: Record<string, { wager: number; payout: number; net: number }> } | null;
|
||||||
|
roomOptions?: { betAmount?: number; timeControl?: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GameUIPlugin {
|
export interface GameUIPlugin {
|
||||||
slug: string;
|
slug: string;
|
||||||
name: string;
|
name: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
|
tagline: string;
|
||||||
|
description: string;
|
||||||
|
minPlayers: number;
|
||||||
maxPlayers: number;
|
maxPlayers: number;
|
||||||
|
/** If true, the host must manually start the game. */
|
||||||
|
manualStart?: boolean;
|
||||||
component: ComponentType<GameUIProps>;
|
component: ComponentType<GameUIProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,3 +39,31 @@ export const gameUIRegistry = {
|
|||||||
return Array.from(plugins.values());
|
return Array.from(plugins.values());
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── Register game UI plugins ──
|
||||||
|
|
||||||
|
import { ChessGame } from "./chess/ChessGame";
|
||||||
|
import { BlackjackGame } from "./blackjack/BlackjackGame";
|
||||||
|
|
||||||
|
gameUIRegistry.register({
|
||||||
|
slug: "chess",
|
||||||
|
name: "Chess",
|
||||||
|
icon: "\u265A",
|
||||||
|
tagline: "Head-to-head duels with clock and wager options.",
|
||||||
|
description: "Challenge another player, choose a time control, and launch a clean board built for focused play.",
|
||||||
|
minPlayers: 2,
|
||||||
|
maxPlayers: 2,
|
||||||
|
component: ChessGame,
|
||||||
|
});
|
||||||
|
|
||||||
|
gameUIRegistry.register({
|
||||||
|
slug: "blackjack",
|
||||||
|
name: "Blackjack",
|
||||||
|
icon: "\uD83C\uDCA1",
|
||||||
|
tagline: "Live table rounds with seating, betting, and spectator support.",
|
||||||
|
description: "Open a table, set the stake, and manage a shared room where players can buy in and queue for the next hand.",
|
||||||
|
minPlayers: 1,
|
||||||
|
maxPlayers: 6,
|
||||||
|
manualStart: true,
|
||||||
|
component: BlackjackGame,
|
||||||
|
});
|
||||||
|
|||||||
@@ -77,3 +77,44 @@ body {
|
|||||||
* {
|
* {
|
||||||
border-color: var(--color-border);
|
border-color: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Custom Animations */
|
||||||
|
@keyframes pulse-slow {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.7; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-pulse-slow {
|
||||||
|
animation: pulse-slow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slide-in animation for result banner */
|
||||||
|
@keyframes slideInFromBottom {
|
||||||
|
from {
|
||||||
|
transform: translateY(1rem);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-in.slide-in-from-bottom-4 {
|
||||||
|
animation: slideInFromBottom 0.3s ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blackjack-card-deal {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(16px) scale(0.94) rotate(-5deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1) rotate(0deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.blackjack-card-deal {
|
||||||
|
animation: blackjack-card-deal 320ms cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,11 +19,12 @@ export function useAuth(): AuthState & { logout: () => Promise<void> } {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch("/auth/me", { credentials: "same-origin" })
|
fetch("/auth/me", { credentials: "same-origin" })
|
||||||
.then((r) => r.json())
|
.then((r) => r.json())
|
||||||
.then((data: { authenticated: boolean; enrolled: boolean; user?: AuthUser }) => {
|
.then((data) => {
|
||||||
|
const auth = data as { authenticated: boolean; enrolled: boolean; user?: AuthUser };
|
||||||
setState({
|
setState({
|
||||||
loading: false,
|
loading: false,
|
||||||
user: data.authenticated ? data.user! : null,
|
user: auth.authenticated ? auth.user ?? null : null,
|
||||||
enrolled: data.enrolled ?? true,
|
enrolled: auth.enrolled ?? true,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => setState({ loading: false, user: null, enrolled: true }));
|
.catch(() => setState({ loading: false, user: null, enrolled: true }));
|
||||||
|
|||||||
@@ -7,41 +7,59 @@ interface PlayerInfo {
|
|||||||
username: string;
|
username: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RoundResult {
|
||||||
|
settlements: Record<string, { wager: number; payout: number; net: number }>;
|
||||||
|
}
|
||||||
|
|
||||||
interface GameRoomState {
|
interface GameRoomState {
|
||||||
gameState: unknown;
|
gameState: unknown;
|
||||||
players: PlayerInfo[];
|
players: PlayerInfo[];
|
||||||
spectators: PlayerInfo[];
|
spectators: PlayerInfo[];
|
||||||
roomStatus: "connecting" | "waiting" | "playing" | "finished" | "not_found";
|
roomStatus: "connecting" | "waiting" | "playing" | "finished" | "not_found";
|
||||||
isSpectator: boolean;
|
isSpectator: boolean;
|
||||||
gameOver: { winner: string | null; reason: string } | null;
|
gameOver: { winner: string | null; reason: string; payout?: { amount: number; refunded?: boolean } } | null;
|
||||||
|
roundResult: RoundResult | null;
|
||||||
error: string | null;
|
error: string | null;
|
||||||
sessionReplaced: boolean;
|
sessionReplaced: boolean;
|
||||||
|
roomOptions: { betAmount?: number; timeControl?: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useGameRoom(roomId: string, userId: string, role?: string, preferAs: "player" | "spectator" = "player") {
|
function createInitialRoomState(): GameRoomState {
|
||||||
const { send, subscribe, connected } = useWebSocket();
|
return {
|
||||||
const navigate = useNavigate();
|
|
||||||
const navigateRef = useRef(navigate);
|
|
||||||
const errorTimerRef = useRef<ReturnType<typeof setTimeout>>();
|
|
||||||
useEffect(() => {
|
|
||||||
navigateRef.current = navigate;
|
|
||||||
}, [navigate]);
|
|
||||||
useEffect(() => () => clearTimeout(errorTimerRef.current), []);
|
|
||||||
|
|
||||||
const [state, setState] = useState<GameRoomState>({
|
|
||||||
gameState: null,
|
gameState: null,
|
||||||
players: [],
|
players: [],
|
||||||
spectators: [],
|
spectators: [],
|
||||||
roomStatus: "connecting",
|
roomStatus: "connecting",
|
||||||
isSpectator: false,
|
isSpectator: false,
|
||||||
gameOver: null,
|
gameOver: null,
|
||||||
|
roundResult: null,
|
||||||
error: null,
|
error: null,
|
||||||
sessionReplaced: false,
|
sessionReplaced: false,
|
||||||
});
|
roomOptions: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useGameRoom(roomId: string, userId: string, role?: string, preferAs: "player" | "spectator" = "player") {
|
||||||
|
const { send, subscribe, connected } = useWebSocket();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const navigateRef = useRef(navigate);
|
||||||
|
const errorTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
navigateRef.current = navigate;
|
||||||
|
}, [navigate]);
|
||||||
|
useEffect(() => () => {
|
||||||
|
if (errorTimerRef.current !== null) {
|
||||||
|
clearTimeout(errorTimerRef.current);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [state, setState] = useState<GameRoomState>(() => createInitialRoomState());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!connected) return;
|
if (!connected) return;
|
||||||
|
|
||||||
|
setState(createInitialRoomState());
|
||||||
|
|
||||||
send({ type: "JOIN_ROOM", roomId, preferAs, role: role ?? "player" });
|
send({ type: "JOIN_ROOM", roomId, preferAs, role: role ?? "player" });
|
||||||
|
|
||||||
const unsubscribe = subscribe((msg: any) => {
|
const unsubscribe = subscribe((msg: any) => {
|
||||||
@@ -56,23 +74,46 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
|
|||||||
players: msg.players ?? prev.players,
|
players: msg.players ?? prev.players,
|
||||||
spectators: msg.spectators ?? prev.spectators,
|
spectators: msg.spectators ?? prev.spectators,
|
||||||
gameState: msg.state !== undefined ? msg.state : prev.gameState,
|
gameState: msg.state !== undefined ? msg.state : prev.gameState,
|
||||||
|
roomOptions: msg.roomOptions ?? prev.roomOptions,
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "GAME_STATE":
|
case "GAME_STATE":
|
||||||
setState(prev => ({
|
// Authoritative player view — sent directly to this player
|
||||||
...prev,
|
setState(prev => {
|
||||||
gameState: msg.state,
|
// Clear round result when a new betting phase starts
|
||||||
roomStatus: prev.roomStatus === "finished" ? "finished" : "playing",
|
const phase = (msg.state as any)?.phase;
|
||||||
}));
|
const roundResult = phase === "betting" ? null : prev.roundResult;
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
gameState: msg.state,
|
||||||
|
roundResult,
|
||||||
|
roomStatus: prev.roomStatus === "finished" ? "finished" : "playing",
|
||||||
|
};
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "GAME_STARTED":
|
case "GAME_STARTED":
|
||||||
setState(prev => ({ ...prev, gameState: msg.state, roomStatus: "playing" }));
|
// Broadcast with spectator view — only use for state if we're a spectator
|
||||||
|
// (players get their own GAME_STATE via direct send)
|
||||||
|
setState(prev => ({
|
||||||
|
...prev,
|
||||||
|
gameState: prev.isSpectator ? msg.state : prev.gameState,
|
||||||
|
roomStatus: "playing",
|
||||||
|
}));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "GAME_UPDATE":
|
case "GAME_UPDATE":
|
||||||
setState(prev => ({ ...prev, gameState: msg.state }));
|
// Broadcast with spectator view — only update state for spectators
|
||||||
|
setState(prev => {
|
||||||
|
if (!prev.isSpectator) return prev;
|
||||||
|
const phase = (msg.state as any)?.phase;
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
gameState: msg.state,
|
||||||
|
roundResult: phase === "betting" ? null : prev.roundResult,
|
||||||
|
};
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "PLAYER_JOINED":
|
case "PLAYER_JOINED":
|
||||||
@@ -105,11 +146,18 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
|
|||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "ROUND_SETTLED":
|
||||||
|
setState(prev => ({
|
||||||
|
...prev,
|
||||||
|
roundResult: { settlements: msg.settlements },
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
|
||||||
case "GAME_ENDED":
|
case "GAME_ENDED":
|
||||||
setState(prev => ({
|
setState(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
roomStatus: "finished",
|
roomStatus: "finished",
|
||||||
gameOver: { winner: msg.winner, reason: msg.reason },
|
gameOver: { winner: msg.winner, reason: msg.reason, payout: msg.payout },
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -123,7 +171,9 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
|
|||||||
setTimeout(() => navigateRef.current("/games"), 2000);
|
setTimeout(() => navigateRef.current("/games"), 2000);
|
||||||
} else {
|
} else {
|
||||||
setState(prev => ({ ...prev, error: msg.message }));
|
setState(prev => ({ ...prev, error: msg.message }));
|
||||||
clearTimeout(errorTimerRef.current);
|
if (errorTimerRef.current !== null) {
|
||||||
|
clearTimeout(errorTimerRef.current);
|
||||||
|
}
|
||||||
errorTimerRef.current = setTimeout(() => {
|
errorTimerRef.current = setTimeout(() => {
|
||||||
setState(prev => ({ ...prev, error: null }));
|
setState(prev => ({ ...prev, error: null }));
|
||||||
}, 5000);
|
}, 5000);
|
||||||
@@ -136,7 +186,7 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
|
|||||||
send({ type: "LEAVE_ROOM", roomId });
|
send({ type: "LEAVE_ROOM", roomId });
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
};
|
};
|
||||||
}, [roomId, connected, userId, send, subscribe]);
|
}, [connected, preferAs, role, roomId, send, subscribe, userId]);
|
||||||
|
|
||||||
const sendAction = useCallback((action: unknown) => {
|
const sendAction = useCallback((action: unknown) => {
|
||||||
const sent = send({ type: "GAME_ACTION", roomId, action });
|
const sent = send({ type: "GAME_ACTION", roomId, action });
|
||||||
@@ -160,5 +210,9 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
|
|||||||
send({ type: "FILL_ROOM", roomId });
|
send({ type: "FILL_ROOM", roomId });
|
||||||
}, [roomId, send]);
|
}, [roomId, send]);
|
||||||
|
|
||||||
return { ...state, sendAction, leaveRoom, rejoin, fillRoom };
|
const startGame = useCallback(() => {
|
||||||
|
send({ type: "START_GAME", roomId });
|
||||||
|
}, [roomId, send]);
|
||||||
|
|
||||||
|
return { ...state, sendAction, leaveRoom, rejoin, fillRoom, startGame };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ function rgbToHex(r: number, g: number, b: number): string {
|
|||||||
function hexToRgb(hex: string): [number, number, number] | null {
|
function hexToRgb(hex: string): [number, number, number] | null {
|
||||||
const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex.trim());
|
const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex.trim());
|
||||||
if (!m) return null;
|
if (!m) return null;
|
||||||
return [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)];
|
return [parseInt(m[1]!, 16), parseInt(m[2]!, 16), parseInt(m[3]!, 16)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -380,7 +380,7 @@ function AiRemoveTab({ imageFile, imageSrc, onClear }: {
|
|||||||
</div>
|
</div>
|
||||||
<div className="bg-card rounded-xl overflow-hidden">
|
<div className="bg-card rounded-xl overflow-hidden">
|
||||||
{resultUrl ? (
|
{resultUrl ? (
|
||||||
<div style={BG_PRESETS[bgPreset].style}>
|
<div style={BG_PRESETS[bgPreset]!.style}>
|
||||||
<img src={resultUrl} className="w-full block" alt="Result" />
|
<img src={resultUrl} className="w-full block" alt="Result" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -528,7 +528,7 @@ export function BackgroundRemoval() {
|
|||||||
const x = Math.floor((e.clientX - rect.left) * scaleX);
|
const x = Math.floor((e.clientX - rect.left) * scaleX);
|
||||||
const y = Math.floor((e.clientY - rect.top) * scaleY);
|
const y = Math.floor((e.clientY - rect.top) * scaleY);
|
||||||
const px = canvas.getContext("2d")!.getImageData(x, y, 1, 1).data;
|
const px = canvas.getContext("2d")!.getImageData(x, y, 1, 1).data;
|
||||||
setKeyColor([px[0], px[1], px[2]]);
|
setKeyColor([px[0]!, px[1]!, px[2]!]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleHexInput = (v: string) => {
|
const handleHexInput = (v: string) => {
|
||||||
@@ -832,7 +832,7 @@ export function BackgroundRemoval() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-card rounded-xl overflow-hidden">
|
<div className="bg-card rounded-xl overflow-hidden">
|
||||||
<div style={BG_PRESETS[bgPreset].style} className={cn("w-full", !hasResult && "hidden")}>
|
<div style={BG_PRESETS[bgPreset]!.style} className={cn("w-full", !hasResult && "hidden")}>
|
||||||
<canvas ref={glCanvasRef} className="w-full block" />
|
<canvas ref={glCanvasRef} className="w-full block" />
|
||||||
</div>
|
</div>
|
||||||
{!hasResult && (
|
{!hasResult && (
|
||||||
|
|||||||