SPB Git

spb/spinza

Public

games: theme-specific symbol vocabulary (45 new shapes) replaces generic royals/cubes across the library; presentation exposed on game cards

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Sep 8, 2026) parent f61a85f Browse files

21 changed files +1,672 −632

modified apps/web/src/components/game/symbol-art.ts +356 −0
@@ -310,6 +310,362 @@ export function drawSymbol(style: SymbolStyle, opts: SymbolArtOptions): Containe
310 310 g.poly([0, 0, r * 0.87, -r * 0.5, r * 0.87, r * 0.5, 0, r]).fill(darken(style.color, 0.3));
311 311 break;
312 312 }
313 + /* ----------------------------------------------------------- themed set */
314 + case "lock": {
315 + g.roundRect(-r * 0.75, -r * 0.1, r * 1.5, r * 1.1, r * 0.2).fill(main);
316 + g.arc(0, -r * 0.15, r * 0.5, Math.PI, 0).stroke({ color: accent, width: size * 0.05 });
317 + g.circle(0, r * 0.35, r * 0.16).fill(0x0b0d14);
318 + g.rect(-r * 0.06, r * 0.35, r * 0.12, r * 0.35).fill(0x0b0d14);
319 + break;
320 + }
321 + case "key": {
322 + g.circle(-r * 0.45, -r * 0.35, r * 0.42).stroke({ color: main, width: size * 0.06 });
323 + g.moveTo(-r * 0.15, -r * 0.05).lineTo(r * 0.85, r * 0.95).stroke({ color: main, width: size * 0.06 });
324 + g.moveTo(r * 0.45, r * 0.55).lineTo(r * 0.7, r * 0.3).stroke({ color: accent, width: size * 0.05 });
325 + g.moveTo(r * 0.65, r * 0.75).lineTo(r * 0.9, r * 0.5).stroke({ color: accent, width: size * 0.05 });
326 + break;
327 + }
328 + case "code": {
329 + g.roundRect(-r, -r * 0.7, r * 2, r * 1.4, r * 0.15).fill({ color: darken(style.color, 0.45), alpha: 0.95 });
330 + g.roundRect(-r, -r * 0.7, r * 2, r * 1.4, r * 0.15).stroke({ color: main, width: size * 0.02 });
331 + c.addChild(label(style.label ?? "</>", size * 0.26, main, true));
332 + break;
333 + }
334 + case "badge": {
335 + g.roundRect(-r * 0.7, -r, r * 1.4, r * 2, r * 0.18).fill(main);
336 + g.circle(0, -r * 0.4, r * 0.28).fill(0x0b0d14);
337 + g.roundRect(-r * 0.45, r * 0.05, r * 0.9, r * 0.14, r * 0.05).fill(accent);
338 + g.roundRect(-r * 0.45, r * 0.35, r * 0.6, r * 0.14, r * 0.05).fill(accent);
339 + break;
340 + }
341 + case "mask": {
342 + g.ellipse(0, 0, r, r * 0.6).fill(main);
343 + g.ellipse(-r * 0.4, -r * 0.05, r * 0.28, r * 0.18).fill(0x0b0d14);
344 + g.ellipse(r * 0.4, -r * 0.05, r * 0.28, r * 0.18).fill(0x0b0d14);
345 + g.moveTo(-r * 0.95, -r * 0.1).lineTo(-r * 1.3, -r * 0.6).stroke({ color: accent, width: size * 0.025 });
346 + g.moveTo(r * 0.95, -r * 0.1).lineTo(r * 1.3, -r * 0.6).stroke({ color: accent, width: size * 0.025 });
347 + break;
348 + }
349 + case "glove": {
350 + g.roundRect(-r * 0.55, -r * 0.2, r * 1.1, r * 1.1, r * 0.25).fill(main);
351 + for (let i = 0; i < 4; i++) g.roundRect(-r * 0.55 + i * r * 0.28, -r, r * 0.22, r * 0.9, r * 0.1).fill(main);
352 + g.roundRect(-r * 0.9, -r * 0.1, r * 0.35, r * 0.7, r * 0.15).fill(main);
353 + g.roundRect(-r * 0.55, r * 0.6, r * 1.1, r * 0.3, r * 0.08).fill(accent);
354 + break;
355 + }
356 + case "rope": {
357 + g.circle(0, 0, r * 0.75).stroke({ color: main, width: size * 0.09 });
358 + g.circle(0, 0, r * 0.75).stroke({ color: accent, width: size * 0.02, alpha: 0.6 });
359 + for (let i = 0; i < 10; i++) {
360 + const a = (i / 10) * Math.PI * 2;
361 + g.moveTo(Math.cos(a) * r * 0.62, Math.sin(a) * r * 0.62).lineTo(Math.cos(a + 0.3) * r * 0.88, Math.sin(a + 0.3) * r * 0.88).stroke({ color: darken(style.color, 0.3), width: size * 0.02 });
362 + }
363 + break;
364 + }
365 + case "bag": {
366 + g.moveTo(-r * 0.75, r * 0.9).bezierCurveTo(-r * 1.1, -r * 0.3, -r * 0.3, -r * 0.6, 0, -r * 0.55).bezierCurveTo(r * 0.3, -r * 0.6, r * 1.1, -r * 0.3, r * 0.75, r * 0.9).lineTo(-r * 0.75, r * 0.9).fill(main);
367 + g.roundRect(-r * 0.3, -r * 0.95, r * 0.6, r * 0.35, r * 0.1).fill(accent);
368 + g.moveTo(-r * 0.35, r * 0.3).lineTo(r * 0.35, r * 0.3).stroke({ color: darken(style.color, 0.3), width: size * 0.03 });
369 + break;
370 + }
371 + case "lantern": {
372 + g.roundRect(-r * 0.7, -r * 0.6, r * 1.4, r * 1.2, r * 0.45).fill(main);
373 + g.rect(-r * 0.35, -r * 0.9, r * 0.7, r * 0.2).fill(accent);
374 + g.rect(-r * 0.35, r * 0.6, r * 0.7, r * 0.2).fill(accent);
375 + g.moveTo(0, r * 0.8).lineTo(0, r).stroke({ color: accent, width: size * 0.03 });
376 + for (let i = -1; i <= 1; i++) g.moveTo(i * r * 0.3, -r * 0.55).lineTo(i * r * 0.3, r * 0.55).stroke({ color: darken(style.color, 0.3), width: size * 0.015 });
377 + break;
378 + }
379 + case "fan": {
380 + for (let i = -3; i <= 3; i++) {
381 + const a = -Math.PI / 2 + i * 0.28;
382 + g.moveTo(0, r * 0.7).lineTo(Math.cos(a) * r * 1.05, r * 0.7 + Math.sin(a) * r * 1.05).stroke({ color: i % 2 ? main : accent, width: size * 0.07 });
383 + }
384 + g.circle(0, r * 0.7, r * 0.14).fill(0x0b0d14);
385 + break;
386 + }
387 + case "scroll": {
388 + g.roundRect(-r * 0.7, -r * 0.85, r * 1.4, r * 1.7, r * 0.1).fill({ color: accent, alpha: 0.9 });
389 + g.roundRect(-r * 0.85, -r, r * 1.7, r * 0.3, r * 0.15).fill(main);
390 + g.roundRect(-r * 0.85, r * 0.7, r * 1.7, r * 0.3, r * 0.15).fill(main);
391 + for (let i = 0; i < 3; i++) g.rect(-r * 0.45, -r * 0.5 + i * r * 0.32, r * 0.9, r * 0.08).fill({ color: darken(style.color, 0.4), alpha: 0.7 });
392 + break;
393 + }
394 + case "cherry": {
395 + g.moveTo(-r * 0.3, r * 0.3).bezierCurveTo(-r * 0.2, -r * 0.5, r * 0.2, -r * 0.9, r * 0.5, -r).stroke({ color: 0x3ddc97, width: size * 0.035 });
396 + g.moveTo(r * 0.45, r * 0.3).bezierCurveTo(r * 0.5, -r * 0.4, r * 0.5, -r * 0.8, r * 0.5, -r).stroke({ color: 0x3ddc97, width: size * 0.035 });
397 + g.circle(-r * 0.35, r * 0.45, r * 0.42).fill(main);
398 + g.circle(r * 0.45, r * 0.45, r * 0.42).fill(main);
399 + g.circle(-r * 0.5, r * 0.3, r * 0.12).fill({ color: 0xffffff, alpha: 0.6 });
400 + g.circle(r * 0.3, r * 0.3, r * 0.12).fill({ color: 0xffffff, alpha: 0.6 });
401 + break;
402 + }
403 + case "seven": {
404 + g.poly([-r * 0.8, -r, r * 0.8, -r, r * 0.8, -r * 0.55, -r * 0.05, r, -r * 0.55, r, r * 0.2, -r * 0.55, -r * 0.8, -r * 0.55]).fill(main);
405 + g.poly([-r * 0.8, -r, r * 0.8, -r, r * 0.8, -r * 0.55, -r * 0.05, r, -r * 0.55, r, r * 0.2, -r * 0.55, -r * 0.8, -r * 0.55]).stroke({ color: accent, width: size * 0.03 });
406 + break;
407 + }
408 + case "bell": {
409 + g.moveTo(-r * 0.75, r * 0.5).bezierCurveTo(-r * 0.75, -r * 0.5, -r * 0.3, -r * 0.9, 0, -r * 0.9).bezierCurveTo(r * 0.3, -r * 0.9, r * 0.75, -r * 0.5, r * 0.75, r * 0.5).lineTo(-r * 0.75, r * 0.5).fill(main);
410 + g.roundRect(-r * 0.9, r * 0.45, r * 1.8, r * 0.18, r * 0.09).fill(accent);
411 + g.circle(0, r * 0.8, r * 0.15).fill(accent);
412 + g.circle(0, -r * 0.95, r * 0.1).fill(accent);
413 + break;
414 + }
415 + case "joystick": {
416 + g.roundRect(-r * 0.9, r * 0.3, r * 1.8, r * 0.6, r * 0.15).fill(darken(style.color, 0.4));
417 + g.rect(-r * 0.08, -r * 0.4, r * 0.16, r * 0.8).fill(0x9aa3b5);
418 + g.circle(0, -r * 0.55, r * 0.35).fill(main);
419 + g.circle(-r * 0.12, -r * 0.65, r * 0.1).fill({ color: 0xffffff, alpha: 0.5 });
420 + g.circle(r * 0.5, r * 0.6, r * 0.12).fill(accent);
421 + break;
422 + }
423 + case "torii": {
424 + g.rect(-r, -r * 0.85, r * 2, r * 0.18).fill(main);
425 + g.rect(-r * 0.8, -r * 0.5, r * 1.6, r * 0.12).fill(main);
426 + g.rect(-r * 0.6, -r * 0.5, r * 0.16, r * 1.5).fill(main);
427 + g.rect(r * 0.44, -r * 0.5, r * 0.16, r * 1.5).fill(main);
428 + g.rect(-r * 0.08, -r * 0.5, r * 0.16, r * 0.35).fill(accent);
429 + break;
430 + }
431 + case "katana": {
432 + g.moveTo(-r * 0.9, r * 0.9).lineTo(r * 0.7, -r * 0.7).stroke({ color: 0xdfe6f0, width: size * 0.05 });
433 + g.moveTo(-r * 0.9, r * 0.9).lineTo(r * 0.7, -r * 0.7).stroke({ color: 0xffffff, width: size * 0.015, alpha: 0.7 });
434 + g.moveTo(-r * 0.55, r * 0.35).lineTo(-r * 0.25, r * 0.65).stroke({ color: accent, width: size * 0.05 });
435 + g.moveTo(-r * 0.9, r * 0.9).lineTo(-r * 0.6, r * 0.6).stroke({ color: main, width: size * 0.08 });
436 + break;
437 + }
438 + case "cassette": {
439 + g.roundRect(-r, -r * 0.65, r * 2, r * 1.3, r * 0.12).fill(main);
440 + g.roundRect(-r * 0.75, -r * 0.4, r * 1.5, r * 0.55, r * 0.08).fill(darken(style.color, 0.45));
441 + g.circle(-r * 0.4, -r * 0.12, r * 0.16).fill(0x0b0d14).circle(r * 0.4, -r * 0.12, r * 0.16).fill(0x0b0d14);
442 + g.rect(-r * 0.6, r * 0.3, r * 1.2, r * 0.14).fill(accent);
443 + break;
444 + }
445 + case "ankh": {
446 + g.ellipse(0, -r * 0.55, r * 0.35, r * 0.42).stroke({ color: main, width: size * 0.06 });
447 + g.rect(-r * 0.08, -r * 0.1, r * 0.16, r * 1.1).fill(main);
448 + g.rect(-r * 0.7, -r * 0.1, r * 1.4, r * 0.16).fill(main);
449 + g.circle(0, -r * 0.55, r * 0.08).fill(accent);
450 + break;
451 + }
452 + case "scarab": {
453 + g.ellipse(0, r * 0.15, r * 0.6, r * 0.7).fill(main);
454 + g.circle(0, -r * 0.6, r * 0.28).fill(darken(style.color, 0.25));
455 + g.moveTo(0, -r * 0.4).lineTo(0, r * 0.8).stroke({ color: accent, width: size * 0.02 });
456 + for (const sgn of [-1, 1]) {
457 + g.moveTo(sgn * r * 0.55, -r * 0.1).lineTo(sgn * r * 0.95, -r * 0.5).stroke({ color: accent, width: size * 0.035 });
458 + g.moveTo(sgn * r * 0.6, r * 0.3).lineTo(sgn * r * 1.0, r * 0.4).stroke({ color: accent, width: size * 0.035 });
459 + }
460 + break;
461 + }
462 + case "pyramid": {
463 + g.poly([0, -r, r, r * 0.75, -r, r * 0.75]).fill(main);
464 + g.poly([0, -r, r, r * 0.75, 0, r * 0.75]).fill(darken(style.color, 0.25));
465 + g.poly([0, -r, r * 0.25, -r * 0.55, -r * 0.25, -r * 0.55]).fill(accent);
466 + break;
467 + }
468 + case "warning": {
469 + g.poly([0, -r, r * 0.95, r * 0.75, -r * 0.95, r * 0.75]).fill(main);
470 + g.poly([0, -r * 0.6, r * 0.55, r * 0.55, -r * 0.55, r * 0.55]).fill(0x0b0d14);
471 + g.rect(-r * 0.08, -r * 0.3, r * 0.16, r * 0.5).fill(main);
472 + g.circle(0, r * 0.38, r * 0.09).fill(main);
473 + break;
474 + }
475 + case "valve": {
476 + g.circle(0, 0, r * 0.85).stroke({ color: main, width: size * 0.07 });
477 + for (let i = 0; i < 4; i++) {
478 + const a = (i / 4) * Math.PI * 2;
479 + g.moveTo(0, 0).lineTo(Math.cos(a) * r * 0.85, Math.sin(a) * r * 0.85).stroke({ color: main, width: size * 0.05 });
480 + }
481 + g.circle(0, 0, r * 0.25).fill(accent);
482 + break;
483 + }
484 + case "gauge": {
485 + g.arc(0, r * 0.2, r * 0.9, Math.PI, 0).stroke({ color: darken(style.color, 0.3), width: size * 0.09 });
486 + g.arc(0, r * 0.2, r * 0.9, Math.PI, Math.PI * 1.6).stroke({ color: main, width: size * 0.09 });
487 + g.moveTo(0, r * 0.2).lineTo(r * 0.45, -r * 0.5).stroke({ color: accent, width: size * 0.04 });
488 + g.circle(0, r * 0.2, r * 0.12).fill(accent);
489 + break;
490 + }
491 + case "barrel": {
492 + g.roundRect(-r * 0.65, -r * 0.9, r * 1.3, r * 1.8, r * 0.2).fill(main);
493 + g.rect(-r * 0.65, -r * 0.45, r * 1.3, r * 0.12).fill(darken(style.color, 0.4));
494 + g.rect(-r * 0.65, r * 0.33, r * 1.3, r * 0.12).fill(darken(style.color, 0.4));
495 + polygon(g, 0, 0, r * 0.28, 3, -Math.PI / 2).fill(accent);
496 + break;
497 + }
498 + case "wheel": {
499 + g.circle(0, 0, r * 0.95).fill(0x0b0d14);
500 + g.circle(0, 0, r * 0.95).stroke({ color: main, width: size * 0.05 });
501 + g.circle(0, 0, r * 0.6).stroke({ color: accent, width: size * 0.03 });
502 + for (let i = 0; i < 5; i++) {
503 + const a = (i / 5) * Math.PI * 2;
504 + g.moveTo(0, 0).lineTo(Math.cos(a) * r * 0.6, Math.sin(a) * r * 0.6).stroke({ color: accent, width: size * 0.035 });
505 + }
506 + g.circle(0, 0, r * 0.14).fill(main);
507 + break;
508 + }
509 + case "helmet": {
510 + g.arc(0, r * 0.1, r * 0.85, Math.PI, 0).fill(main);
511 + g.rect(-r * 0.85, r * 0.1, r * 1.7, r * 0.45).fill(main);
512 + g.roundRect(-r * 0.6, -r * 0.05, r * 1.4, r * 0.4, r * 0.1).fill(0x0b0d14);
513 + g.moveTo(-r * 0.3, -r * 0.7).lineTo(r * 0.3, -r * 0.7).stroke({ color: accent, width: size * 0.04 });
514 + break;
515 + }
516 + case "flag": {
517 + g.rect(-r * 0.85, -r, r * 0.1, r * 2).fill(0x9aa3b5);
518 + for (let i = 0; i < 4; i++) for (let j = 0; j < 3; j++) g.rect(-r * 0.75 + i * r * 0.4, -r + j * r * 0.33, r * 0.4, r * 0.33).fill((i + j) % 2 ? main : accent);
519 + break;
520 + }
521 + case "trophy": {
522 + g.moveTo(-r * 0.7, -r).lineTo(r * 0.7, -r).lineTo(r * 0.45, r * 0.15).lineTo(-r * 0.45, r * 0.15).fill(main);
523 + g.arc(-r * 0.75, -r * 0.55, r * 0.3, Math.PI * 0.5, Math.PI * 1.5).stroke({ color: main, width: size * 0.04 });
524 + g.arc(r * 0.75, -r * 0.55, r * 0.3, -Math.PI * 0.5, Math.PI * 0.5).stroke({ color: main, width: size * 0.04 });
525 + g.rect(-r * 0.1, r * 0.15, r * 0.2, r * 0.4).fill(main);
526 + g.roundRect(-r * 0.5, r * 0.55, r, r * 0.3, r * 0.06).fill(accent);
527 + break;
528 + }
529 + case "idol": {
530 + g.roundRect(-r * 0.55, -r * 0.9, r * 1.1, r * 1.8, r * 0.15).fill(main);
531 + g.rect(-r * 0.4, -r * 0.55, r * 0.8, r * 0.14).fill(0x0b0d14);
532 + g.rect(-r * 0.4, -r * 0.1, r * 0.8, r * 0.14).fill(0x0b0d14);
533 + g.poly([-r * 0.3, r * 0.35, r * 0.3, r * 0.35, 0, r * 0.7]).fill(accent);
534 + break;
535 + }
536 + case "totem": {
537 + for (let i = 0; i < 3; i++) {
538 + g.roundRect(-r * 0.55, -r + i * r * 0.68, r * 1.1, r * 0.6, r * 0.08).fill(i % 2 ? darken(style.color, 0.25) : main);
539 + g.circle(-r * 0.22, -r * 0.7 + i * r * 0.68, r * 0.08).fill(accent);
540 + g.circle(r * 0.22, -r * 0.7 + i * r * 0.68, r * 0.08).fill(accent);
541 + }
542 + break;
543 + }
544 + case "snake": {
545 + g.moveTo(-r * 0.8, r * 0.8).bezierCurveTo(-r * 0.9, -r * 0.2, r * 0.9, r * 0.6, r * 0.6, -r * 0.5).bezierCurveTo(r * 0.4, -r * 1.0, -r * 0.2, -r * 0.6, 0, -r * 0.3).stroke({ color: main, width: size * 0.08 });
546 + g.circle(0, -r * 0.3, r * 0.2).fill(main);
547 + g.circle(r * 0.06, -r * 0.36, r * 0.06).fill(accent);
548 + break;
549 + }
550 + case "rocket": {
551 + g.moveTo(0, -r).bezierCurveTo(r * 0.6, -r * 0.4, r * 0.5, r * 0.4, r * 0.35, r * 0.6).lineTo(-r * 0.35, r * 0.6).bezierCurveTo(-r * 0.5, r * 0.4, -r * 0.6, -r * 0.4, 0, -r).fill(main);
552 + g.poly([-r * 0.35, r * 0.2, -r * 0.75, r * 0.8, -r * 0.35, r * 0.6]).fill(accent);
553 + g.poly([r * 0.35, r * 0.2, r * 0.75, r * 0.8, r * 0.35, r * 0.6]).fill(accent);
554 + g.circle(0, -r * 0.25, r * 0.18).fill(0x0b0d14);
555 + g.poly([-r * 0.2, r * 0.6, r * 0.2, r * 0.6, 0, r]).fill(0xffb347);
556 + break;
557 + }
558 + case "anchor": {
559 + g.circle(0, -r * 0.75, r * 0.2).stroke({ color: main, width: size * 0.05 });
560 + g.rect(-r * 0.07, -r * 0.55, r * 0.14, r * 1.4).fill(main);
561 + g.rect(-r * 0.55, -r * 0.35, r * 1.1, r * 0.12).fill(main);
562 + g.arc(0, r * 0.15, r * 0.75, Math.PI * 0.15, Math.PI * 0.85).stroke({ color: main, width: size * 0.07 });
563 + g.circle(-r * 0.7, r * 0.4, r * 0.08).fill(accent).circle(r * 0.7, r * 0.4, r * 0.08).fill(accent);
564 + break;
565 + }
566 + case "chest": {
567 + g.roundRect(-r * 0.9, -r * 0.2, r * 1.8, r * 1.0, r * 0.1).fill(main);
568 + g.roundRect(-r * 0.9, -r * 0.75, r * 1.8, r * 0.6, r * 0.3).fill(darken(style.color, 0.2));
569 + g.rect(-r * 0.9, -r * 0.2, r * 1.8, r * 0.12).fill(accent);
570 + g.roundRect(-r * 0.15, -r * 0.2, r * 0.3, r * 0.35, r * 0.05).fill(accent);
571 + break;
572 + }
573 + case "trident": {
574 + g.rect(-r * 0.07, -r * 0.4, r * 0.14, r * 1.4).fill(main);
575 + for (const dx of [-0.5, 0, 0.5]) {
576 + g.moveTo(dx * r, -r * 0.3).lineTo(dx * r, -r).stroke({ color: main, width: size * 0.05 });
577 + g.poly([dx * r - r * 0.12, -r * 0.85, dx * r + r * 0.12, -r * 0.85, dx * r, -r * 1.1]).fill(accent);
578 + }
579 + g.rect(-r * 0.5, -r * 0.4, r, r * 0.12).fill(main);
580 + break;
581 + }
582 + case "shell": {
583 + g.arc(0, r * 0.4, r * 0.95, Math.PI, 0).fill(main);
584 + for (let i = -3; i <= 3; i++) g.moveTo(0, r * 0.4).lineTo(Math.cos(-Math.PI / 2 + i * 0.38) * r * 0.95, r * 0.4 + Math.sin(-Math.PI / 2 + i * 0.38) * r * 0.95).stroke({ color: darken(style.color, 0.25), width: size * 0.02 });
585 + g.rect(-r * 0.95, r * 0.4, r * 1.9, r * 0.18).fill(accent);
586 + break;
587 + }
588 + case "planet": {
589 + g.circle(0, 0, r * 0.7).fill(main);
590 + g.ellipse(0, r * 0.05, r * 1.05, r * 0.28).stroke({ color: accent, width: size * 0.04 });
591 + g.circle(-r * 0.25, -r * 0.25, r * 0.18).fill({ color: 0xffffff, alpha: 0.3 });
592 + g.ellipse(r * 0.15, r * 0.15, r * 0.3, r * 0.12).fill({ color: darken(style.color, 0.25), alpha: 0.8 });
593 + break;
594 + }
595 + case "comet": {
596 + g.moveTo(r * 0.5, -r * 0.5).lineTo(-r * 1.0, r * 0.15).lineTo(-r * 0.6, r * 0.6).lineTo(r * 0.5, -r * 0.5).fill({ color: accent, alpha: 0.5 });
597 + g.moveTo(r * 0.5, -r * 0.5).lineTo(-r * 0.7, r * 0.7).stroke({ color: accent, alpha: 0.4, width: size * 0.05 });
598 + g.circle(r * 0.5, -r * 0.5, r * 0.4).fill(main);
599 + g.circle(r * 0.4, -r * 0.6, r * 0.12).fill({ color: 0xffffff, alpha: 0.6 });
600 + break;
601 + }
602 + case "satellite": {
603 + g.roundRect(-r * 0.3, -r * 0.3, r * 0.6, r * 0.6, r * 0.1).fill(main);
604 + g.rect(-r * 1.0, -r * 0.18, r * 0.6, r * 0.36).fill(accent);
605 + g.rect(r * 0.4, -r * 0.18, r * 0.6, r * 0.36).fill(accent);
606 + for (const x of [-0.8, -0.6, 0.6, 0.8]) g.moveTo(x * r, -r * 0.18).lineTo(x * r, r * 0.18).stroke({ color: darken(style.color, 0.4), width: size * 0.012 });
607 + g.arc(0, -r * 0.55, r * 0.3, Math.PI, 0).stroke({ color: main, width: size * 0.03 });
608 + break;
609 + }
610 + case "pickaxe": {
611 + g.moveTo(-r * 0.7, r * 0.9).lineTo(r * 0.5, -r * 0.4).stroke({ color: 0x8a6d2e, width: size * 0.06 });
612 + g.moveTo(r * 0.0, -r * 0.9).bezierCurveTo(r * 0.7, -r * 0.9, r * 1.0, -r * 0.4, r * 0.95, r * 0.15).lineTo(r * 0.55, -r * 0.35).fill(main);
613 + g.moveTo(r * 0.0, -r * 0.9).lineTo(r * 0.55, -r * 0.35).stroke({ color: accent, width: size * 0.03 });
614 + break;
615 + }
616 + case "ore": {
617 + polygon(g, 0, r * 0.1, r * 0.9, 7).fill(0x3a3f4a);
618 + g.poly([-r * 0.3, -r * 0.1, 0, -r * 0.5, r * 0.35, -r * 0.05, r * 0.1, r * 0.35, -r * 0.25, r * 0.3]).fill(main);
619 + g.poly([-r * 0.3, -r * 0.1, 0, -r * 0.5, 0, 0]).fill(accent);
620 + break;
621 + }
622 + case "drill": {
623 + g.rect(-r * 0.4, -r * 0.9, r * 0.8, r * 0.7).fill(main);
624 + g.poly([-r * 0.4, -r * 0.2, r * 0.4, -r * 0.2, 0, r]).fill(0xc8d0dc);
625 + for (let i = 0; i < 4; i++) g.moveTo(-r * 0.32 + i * r * 0.1, -r * 0.2 + i * r * 0.28).lineTo(r * 0.32 - i * r * 0.1, -r * 0.2 + i * r * 0.28).stroke({ color: 0x5c6270, width: size * 0.015 });
626 + g.rect(-r * 0.15, -r * 0.6, r * 0.3, r * 0.15).fill(accent);
627 + break;
628 + }
629 + case "ingot": {
630 + g.poly([-r * 0.7, -r * 0.3, r * 0.7, -r * 0.3, r * 0.95, r * 0.5, -r * 0.95, r * 0.5]).fill(main);
631 + g.poly([-r * 0.7, -r * 0.3, r * 0.7, -r * 0.3, r * 0.5, 0, -r * 0.5, 0]).fill(accent);
632 + g.moveTo(-r * 0.5, r * 0.15).lineTo(r * 0.5, r * 0.15).stroke({ color: darken(style.color, 0.3), width: size * 0.02 });
633 + break;
634 + }
635 + case "hourglass": {
636 + g.poly([-r * 0.7, -r, r * 0.7, -r, r * 0.1, 0, r * 0.7, r, -r * 0.7, r, -r * 0.1, 0]).stroke({ color: main, width: size * 0.04 });
637 + g.poly([-r * 0.45, -r * 0.8, r * 0.45, -r * 0.8, 0, -r * 0.1]).fill({ color: accent, alpha: 0.8 });
638 + g.poly([-r * 0.55, r * 0.9, r * 0.55, r * 0.9, 0, r * 0.35]).fill({ color: accent, alpha: 0.8 });
639 + break;
640 + }
641 + case "igloo": {
642 + g.arc(0, r * 0.4, r * 0.95, Math.PI, 0).fill(main);
643 + g.rect(-r * 0.95, r * 0.4, r * 1.9, r * 0.2).fill(main);
644 + g.arc(-r * 0.3, r * 0.5, r * 0.35, Math.PI, 0).fill(0x0b0d14);
645 + for (let i = 0; i < 3; i++) g.arc(0, r * 0.4, r * (0.35 + i * 0.25), Math.PI, 0).stroke({ color: accent, width: size * 0.015, alpha: 0.6 });
646 + break;
647 + }
648 + case "paw": {
649 + g.ellipse(0, r * 0.35, r * 0.55, r * 0.45).fill(main);
650 + for (const [dx, dy] of [[-0.6, -0.25], [-0.22, -0.6], [0.22, -0.6], [0.6, -0.25]]) g.circle(dx * r, dy * r, r * 0.2).fill(main);
651 + g.ellipse(0, r * 0.4, r * 0.3, r * 0.22).fill({ color: accent, alpha: 0.6 });
652 + break;
653 + }
654 + case "fish": {
655 + g.ellipse(-r * 0.1, 0, r * 0.7, r * 0.42).fill(main);
656 + g.poly([r * 0.5, 0, r, -r * 0.45, r, r * 0.45]).fill(accent);
657 + g.circle(-r * 0.5, -r * 0.1, r * 0.08).fill(0x0b0d14);
658 + g.moveTo(-r * 0.2, -r * 0.4).bezierCurveTo(0, -r * 0.7, r * 0.2, -r * 0.5, r * 0.25, -r * 0.3).fill({ color: accent, alpha: 0.8 });
659 + break;
660 + }
661 + case "meteor": {
662 + polygon(g, r * 0.2, r * 0.2, r * 0.6, 7, 0.3).fill(main);
663 + g.circle(r * 0.35, 0, r * 0.12).fill({ color: darken(style.color, 0.3) });
664 + g.circle(0, r * 0.4, r * 0.1).fill({ color: darken(style.color, 0.3) });
665 + g.moveTo(-r * 0.2, -r * 0.3).lineTo(-r * 1.0, -r * 1.0).stroke({ color: accent, alpha: 0.6, width: size * 0.06 });
666 + g.moveTo(r * 0.1, -r * 0.55).lineTo(-r * 0.5, -r * 1.05).stroke({ color: accent, alpha: 0.4, width: size * 0.04 });
667 + break;
668 + }
313 669 }
314 670 c.addChild(g);
315 671 return c;
modified apps/web/src/components/lobby/game-art.tsx +1167 −533
Large diff (1,892 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/arctic-fortune/index.ts +3 −3
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/cosmic-collapse/index.ts +3 −3
Large diff (12 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/deep-treasure/index.ts +4 −4
Large diff (18 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/diamond-heist/index.ts +10 −10
Large diff (28 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/dragon-core/index.ts +1 −1
Large diff (8 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/golden-emperor/index.ts +7 −7
Large diff (20 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/inferno-reels/index.ts +3 −3
Large diff (16 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/lucky-circuit/index.ts +4 −4
Large diff (16 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/midnight-tokyo/index.ts +8 −8
Large diff (23 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/moonbase-77/index.ts +6 −6
Large diff (18 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/neon-vault/index.ts +7 −7
Large diff (24 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/pharaoh-protocol/index.ts +8 −8
Large diff (25 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/quantum-jackpot/index.ts +4 −4
Large diff (15 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/reel-reactor/index.ts +5 −5
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/royal-circuit/index.ts +10 −10
Large diff (27 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/void-miner/index.ts +4 −4
Large diff (20 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/wild-temple/index.ts +8 −8
Large diff (24 lines) is not loaded by default to keep the page fast.Load diff
modified games/src/zero-gravity/index.ts +3 −3
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff
modified packages/game-core/src/types.ts +51 −1
Large diff (59 lines) is not loaded by default to keep the page fast.Load diff
Menu

Theme