Created
March 6, 2026 10:27
-
-
Save VarunBatraIT/216a7d13b8f0a56b8aa86801483d9e2b to your computer and use it in GitHub Desktop.
superhero-lichess
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Lichess Game Play for Kids - Chess Superhero Academy ⚡ | |
| // @namespace vm-lichess-game-play-kids-3 | |
| // @version 1.0 | |
| // @description Your child is a superhero-in-training! Activate superpowers, fill the energy bar, and transform into a super hero before making each move. Choose Captain Checkmate, Wonder Knight, Pawn Power, or Bishop Wizard. | |
| // @match https://lichess.org/* | |
| // @run-at document-start | |
| // @grant none | |
| // ==/UserScript== | |
| "use strict"; | |
| (() => { | |
| var __defProp = Object.defineProperty; | |
| var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); | |
| // src/shared/utils.ts | |
| function randomItem(array) { | |
| return array[Math.floor(Math.random() * array.length)]; | |
| } | |
| __name(randomItem, "randomItem"); | |
| // src/shared/kids-font-config.ts | |
| var KIDS_FONT_FAMILY = "'Nunito', 'Segoe UI', 'Verdana', sans-serif"; | |
| var BASE = { | |
| characterEmoji: 48, | |
| speechBubble: 15, | |
| title: 18, | |
| subtitle: 14, | |
| tip: 14, | |
| timer: 20, | |
| button: 14, | |
| smallLabel: 12, | |
| sectionEmoji: 28, | |
| pickerTitle: 24, | |
| pickerEmoji: 38, | |
| pickerName: 14, | |
| pickerSubtext: 12 | |
| }; | |
| function computeScale() { | |
| const vh = window.innerHeight || 900; | |
| return Math.max(0.7, Math.min(1.15, vh / 900)); | |
| } | |
| __name(computeScale, "computeScale"); | |
| var _cache = null; | |
| var _cacheVh = 0; | |
| function getKidsFontSizes() { | |
| const vh = window.innerHeight || 900; | |
| if (_cache && _cacheVh === vh) | |
| return _cache; | |
| const s = computeScale(); | |
| const px = /* @__PURE__ */ __name((b) => `${Math.round(b * s)}px`, "px"); | |
| _cache = { | |
| characterEmoji: px(BASE.characterEmoji), | |
| speechBubble: px(BASE.speechBubble), | |
| title: px(BASE.title), | |
| subtitle: px(BASE.subtitle), | |
| tip: px(BASE.tip), | |
| timer: px(BASE.timer), | |
| button: px(BASE.button), | |
| smallLabel: px(BASE.smallLabel), | |
| sectionEmoji: px(BASE.sectionEmoji), | |
| pickerTitle: px(BASE.pickerTitle), | |
| pickerEmoji: px(BASE.pickerEmoji), | |
| pickerName: px(BASE.pickerName), | |
| pickerSubtext: px(BASE.pickerSubtext) | |
| }; | |
| _cacheVh = vh; | |
| return _cache; | |
| } | |
| __name(getKidsFontSizes, "getKidsFontSizes"); | |
| function injectKidsFont(prefix) { | |
| const id = `${prefix}-font-link`; | |
| if (document.getElementById(id)) | |
| return; | |
| const link = document.createElement("link"); | |
| link.id = id; | |
| link.rel = "stylesheet"; | |
| link.href = "https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800;900&display=swap"; | |
| document.head.appendChild(link); | |
| } | |
| __name(injectKidsFont, "injectKidsFont"); | |
| function getKidsSpeechBubbleCSS(prefix) { | |
| return ` | |
| /* Zero-height wrapper keeps bubble out of layout flow */ | |
| .${prefix}-bubble-wrapper { | |
| position: relative; | |
| width: 100%; | |
| height: 0; | |
| overflow: visible; | |
| z-index: 50; | |
| } | |
| .${prefix}-comic-bubble { | |
| position: absolute; | |
| top: 2px; | |
| left: 0; | |
| right: 0; | |
| background: rgba(15, 23, 42, 0.95); | |
| border: 2px solid rgba(255, 255, 255, 0.25); | |
| border-radius: 14px; | |
| padding: 8px 12px; | |
| backdrop-filter: blur(10px); | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.5); | |
| opacity: 0; | |
| transform: translateY(-4px) scale(0.96); | |
| transition: opacity 0.25s ease, transform 0.25s ease; | |
| pointer-events: none; | |
| } | |
| .${prefix}-comic-bubble.visible { | |
| opacity: 1; | |
| transform: translateY(0) scale(1); | |
| pointer-events: auto; | |
| } | |
| .${prefix}-comic-bubble::before { | |
| content: ''; | |
| position: absolute; | |
| top: -10px; | |
| left: 28px; | |
| width: 0; height: 0; | |
| border-left: 8px solid transparent; | |
| border-right: 8px solid transparent; | |
| border-bottom: 10px solid rgba(255, 255, 255, 0.25); | |
| } | |
| .${prefix}-comic-bubble::after { | |
| content: ''; | |
| position: absolute; | |
| top: -8px; | |
| left: 29px; | |
| width: 0; height: 0; | |
| border-left: 7px solid transparent; | |
| border-right: 7px solid transparent; | |
| border-bottom: 8px solid rgba(15, 23, 42, 0.95); | |
| } | |
| `; | |
| } | |
| __name(getKidsSpeechBubbleCSS, "getKidsSpeechBubbleCSS"); | |
| // src/game-play-for-kids-3/config.ts | |
| var HERO_CHARACTERS = [ | |
| { | |
| id: "captain", | |
| name: "Captain Checkmate", | |
| emoji: "\u{1F9B8}", | |
| transformedEmoji: "\u{1F9B8}\u200D\u2642\uFE0F\u26A1", | |
| color: "#dc2626", | |
| bgGradient: "linear-gradient(135deg, #1a0000 0%, #2d0a0a 50%, #4a1111 100%)", | |
| superpower: "Tactical Vision", | |
| battleCries: [ | |
| "HERO TIME! Let's protect the board! \u{1F9B8}", | |
| "Captain Checkmate reporting for duty! \u2B50", | |
| "Time to use our superpowers! \u{1F4AA}", | |
| "With great pawns comes great responsibility! \u{1F3AF}" | |
| ], | |
| powerUpLines: [ | |
| "POWER ACTIVATED! You're getting stronger! \u26A1", | |
| "Energy rising! Keep going, hero! \u{1F50B}", | |
| "Another power unlocked! AMAZING! \u{1F4AB}", | |
| "Your hero energy is building! \u{1F31F}" | |
| ], | |
| transformLines: [ | |
| "\u{1F31F} FULL POWER! You're UNSTOPPABLE! \u{1F31F}", | |
| "\u26A1 SUPER CHESS HERO MODE ACTIVATED! \u26A1", | |
| "\u{1F4A5} ALL POWERS READY! Make your SUPER MOVE! \u{1F4A5}", | |
| "\u{1F3C6} HERO TRANSFORMATION COMPLETE! GO! \u{1F3C6}" | |
| ], | |
| defeatLines: [ | |
| "Even heroes have setbacks! We'll rise again! \u{1F4AA}", | |
| "A true hero never gives up! Let's go! \u{1F9B8}", | |
| "Recharging... heroes always come back stronger! \u26A1", | |
| "That's OK! Every hero has an origin story! \u{1F4D6}" | |
| ], | |
| quickMoveWarnings: [ | |
| "WAIT! A hero always scans for danger first! \u{1F6E1}\uFE0F", | |
| "HOLD ON! Use your superpowers before moving! \u26A1", | |
| "Heroes don't rush! Activate your powers first! \u{1F9B8}", | |
| "Careful! Check your hero checklist first! \u{1F4CB}" | |
| ] | |
| }, | |
| { | |
| id: "knight", | |
| name: "Wonder Knight", | |
| emoji: "\u{1F434}", | |
| transformedEmoji: "\u{1F434}\u{1F451}", | |
| color: "#7c3aed", | |
| bgGradient: "linear-gradient(135deg, #0f0024 0%, #1a0a3e 50%, #2d1b69 100%)", | |
| superpower: "Jump Vision", | |
| battleCries: [ | |
| "NEIGH! Wonder Knight gallops into action! \u{1F434}", | |
| "Time to jump into the battle! \u265E", | |
| "My knight senses are tingling! \u{1F31F}", | |
| "Let's leap to victory! \u{1F49C}" | |
| ], | |
| powerUpLines: [ | |
| "LEAP POWER growing! Amazing! \u{1F434}\u26A1", | |
| "Your knight energy is rising! \u265E\u{1F4AB}", | |
| "Another power! You're becoming unstoppable! \u{1F31F}", | |
| "Wonder Knight is getting STRONGER! \u{1F4AA}" | |
| ], | |
| transformLines: [ | |
| "\u{1F31F} FULL KNIGHT POWER! L-SHAPED DOMINATION! \u{1F31F}", | |
| "\u26A1 WONDER KNIGHT ULTIMATE FORM! \u26A1", | |
| "\u{1F4A5} ALL POWERS READY! GALLOP TO VICTORY! \u{1F4A5}", | |
| "\u{1F3C6} KNIGHT CHAMPION MODE! Make your move! \u{1F3C6}" | |
| ], | |
| defeatLines: [ | |
| "Even knights stumble sometimes! Mount up again! \u{1F434}", | |
| "A true knight never surrenders! \u{1F49C}", | |
| "Recharging horse power... back in action! \u26A1", | |
| "Knights always find another path! Keep going! \u265E" | |
| ], | |
| quickMoveWarnings: [ | |
| "WHOA! A knight looks before leaping! \u{1F434}", | |
| "HALT! Activate your powers first, knight! \u26A1", | |
| "Wait! Even Wonder Knight checks the board! \u{1F6E1}\uFE0F", | |
| "Don't gallop too fast! Use your vision first! \u{1F440}" | |
| ] | |
| }, | |
| { | |
| id: "pawn", | |
| name: "Pawn Power", | |
| emoji: "\u265F\uFE0F", | |
| transformedEmoji: "\u265F\uFE0F\u{1F478}", | |
| color: "#16a34a", | |
| bgGradient: "linear-gradient(135deg, #001a0a 0%, #0a2e1a 50%, #144d2e 100%)", | |
| superpower: "Promotion Dream", | |
| battleCries: [ | |
| "Small but MIGHTY! Pawn Power is here! \u265F\uFE0F", | |
| "Every champion starts as a pawn! Let's go! \u{1F331}", | |
| "I may be small but I dream BIG! \u{1F451}", | |
| "Pawn Power ACTIVATE! \u{1F49A}" | |
| ], | |
| powerUpLines: [ | |
| "Growing stronger! One step at a time! \u265F\uFE0F\u26A1", | |
| "Pawn Power increasing! You're amazing! \u{1F49A}", | |
| "Another power! Getting closer to promotion! \u{1F451}", | |
| "Your pawn energy is GLOWING! \u{1F31F}" | |
| ], | |
| transformLines: [ | |
| "\u{1F31F} FULL PAWN POWER! PROMOTION TIME! \u{1F451}", | |
| "\u26A1 PAWN HERO ULTIMATE FORM! YOU'RE A QUEEN! \u26A1", | |
| "\u{1F4A5} ALL POWERS READY! Make your CHAMPION move! \u{1F4A5}", | |
| "\u{1F3C6} FROM PAWN TO QUEEN! UNSTOPPABLE! \u{1F3C6}" | |
| ], | |
| defeatLines: [ | |
| "Even pawns step back to step forward! \u265F\uFE0F", | |
| "Remember: pawns become QUEENS! Keep going! \u{1F451}", | |
| "Small setback, BIG comeback! \u{1F49A}", | |
| "A pawn's journey is never easy, but always worth it! \u{1F31F}" | |
| ], | |
| quickMoveWarnings: [ | |
| "WAIT! Even brave pawns look before advancing! \u265F\uFE0F", | |
| "HOLD! Use your powers first, little hero! \u26A1", | |
| "Careful! Check the path ahead! \u{1F440}", | |
| "Pawns move carefully! Scan first! \u{1F6E1}\uFE0F" | |
| ] | |
| }, | |
| { | |
| id: "wizard", | |
| name: "Bishop Wizard", | |
| emoji: "\u{1F9D9}", | |
| transformedEmoji: "\u{1F9D9}\u2728", | |
| color: "#0ea5e9", | |
| bgGradient: "linear-gradient(135deg, #000a1a 0%, #0a1e3d 50%, #0d2b52 100%)", | |
| superpower: "Diagonal Magic", | |
| battleCries: [ | |
| "ABRACACHESS! The Wizard enters the game! \u{1F9D9}", | |
| "My crystal ball shows the way! \u{1F52E}", | |
| "Time for some diagonal MAGIC! \u2728", | |
| "The chess wizard casts a spell of thinking! \u{1F31F}" | |
| ], | |
| powerUpLines: [ | |
| "MAGICAL power growing! Fantastic! \u{1F9D9}\u26A1", | |
| "Your wizard energy sparkles! \u2728", | |
| "Another spell learned! You're brilliant! \u{1F52E}", | |
| "The magic is getting STRONGER! \u{1F4AB}" | |
| ], | |
| transformLines: [ | |
| "\u{1F31F} FULL WIZARD POWER! ULTIMATE MAGIC! \u{1F31F}", | |
| "\u26A1 ARCH-WIZARD MODE! DIAGONAL MASTERY! \u26A1", | |
| "\u{1F4A5} ALL SPELLS READY! Cast your BEST move! \u{1F4A5}", | |
| "\u{1F3C6} GRAND WIZARD! The board bows to you! \u{1F3C6}" | |
| ], | |
| defeatLines: [ | |
| "Even wizards miscast sometimes! Try again! \u{1F9D9}", | |
| "The magic will return! Never give up! \u2728", | |
| "Recharging mana... wizards are resilient! \u{1F52E}", | |
| "A true wizard learns from every spell! \u{1F4DA}" | |
| ], | |
| quickMoveWarnings: [ | |
| "WAIT! A wizard always checks the omens first! \u{1F9D9}", | |
| "HOLD! Cast your scanning spells first! \u{1F52E}", | |
| "Don't rush the magic! Think first! \u2728", | |
| "Wise wizards are patient wizards! \u{1F31F}" | |
| ] | |
| } | |
| ]; | |
| var STORAGE_KEY = "vm-game-play-kids-3-hero"; | |
| var SUPERPOWERS = [ | |
| { | |
| id: "danger_sense", | |
| name: "Spot Immediate Threats", | |
| emoji: "\u{1F6E1}\uFE0F", | |
| description: "Scan for threats & undefended material!", | |
| heroShout: "DANGER SENSE ACTIVATED! Can any enemy piece capture yours? SCAN for undefended material! \u{1F6E1}\uFE0F", | |
| color: "#ef4444", | |
| energyCost: 1, | |
| timer: 10 | |
| }, | |
| { | |
| id: "guardian_shield", | |
| name: "Find Hanging Pieces", | |
| emoji: "\u{1F530}", | |
| description: "Check if all your pieces have defenders!", | |
| heroShout: "SHIELD UP! Find hanging pieces with NO protection! Every piece needs a defender! \u{1F530}", | |
| color: "#f97316", | |
| energyCost: 1, | |
| timer: 10 | |
| }, | |
| { | |
| id: "royal_guard", | |
| name: "King's Safety & Check Angles", | |
| emoji: "\u{1F451}", | |
| description: "Is your King safe? Can you check theirs?", | |
| heroShout: "ROYAL GUARD POWER! Is your King safe? Look for check angles on THEIR King! \u{1F451}", | |
| color: "#eab308", | |
| energyCost: 2, | |
| timer: 10 | |
| }, | |
| { | |
| id: "fork_fury", | |
| name: "Forks (Double Attack)", | |
| emoji: "\u2694\uFE0F", | |
| description: "Find a move that attacks TWO pieces at once!", | |
| heroShout: "FORK FURY! Can any of your pieces attack TWO enemies at once? That's a FORK \u2014 DOUBLE DAMAGE! \u2694\uFE0F", | |
| color: "#22c55e", | |
| energyCost: 2, | |
| timer: 12 | |
| }, | |
| { | |
| id: "xray_vision", | |
| name: "Pins & Skewers", | |
| emoji: "\u{1F441}\uFE0F", | |
| description: "See through pieces! Find pins and skewers!", | |
| heroShout: "X-RAY VISION ON! Look THROUGH pieces! Find PINS (piece stuck guarding) and SKEWERS (attack in a line)! \u{1F441}\uFE0F", | |
| color: "#06b6d4", | |
| energyCost: 2, | |
| timer: 12 | |
| }, | |
| { | |
| id: "safe_landing", | |
| name: "Safe Square Check", | |
| emoji: "\u{1F3E0}", | |
| description: "Make sure your landing zone is safe!", | |
| heroShout: "SAFE LANDING CHECK! Where will your piece land? Is that square SAFE from enemy fire? Check! \u{1F3E0}", | |
| color: "#8b5cf6", | |
| energyCost: 1, | |
| timer: 10 | |
| }, | |
| { | |
| id: "future_sight", | |
| name: "Opponent's Plan (Prophylaxis)", | |
| emoji: "\u{1F52E}", | |
| description: "Predict what your opponent will do next!", | |
| heroShout: "FUTURE SIGHT! If you move there, what will THEY do? This is called PROPHYLAXIS \u2014 seeing the FUTURE! \u{1F52E}", | |
| color: "#ec4899", | |
| energyCost: 3, | |
| timer: 12 | |
| }, | |
| { | |
| id: "ultimate_move", | |
| name: "Best Move (Checks, Captures, Threats)", | |
| emoji: "\u{1F4A5}", | |
| description: "Choose your BEST and STRONGEST move!", | |
| heroShout: "ULTIMATE MOVE TIME! Think: Checks first, then Captures, then Threats! Pick the STRONGEST! GO! \u{1F4A5}", | |
| color: "#f59e0b", | |
| energyCost: 3, | |
| timer: 14 | |
| } | |
| ]; | |
| var HERO_TIPS = [ | |
| "\u{1F9B8} Always scan for Immediate Threats BEFORE acting!", | |
| "\u26A1 Use ALL your powers before making a move!", | |
| "\u{1F6E1}\uFE0F Protect your pieces! Don't leave Hanging Pieces!", | |
| "\u{1F451} King's Safety is #1! Always keep the King safe!", | |
| "\u2694\uFE0F Forks (Double Attacks) are a hero's best weapon!", | |
| "\u{1F441}\uFE0F Look for Pins & Skewers \u2014 X-Ray power!", | |
| "\u{1F3E0} Safe Square: always check the landing zone!", | |
| "\u{1F52E} Prophylaxis: predict your opponent's next move!", | |
| "\u{1F4AA} Heroes are PATIENT! Think before you act!", | |
| "\u{1F3AF} Control the CENTER \u2014 Open Files & Ranks!", | |
| "\u{1F434} Knights love Outposts! They jump over walls!", | |
| "\u{1F3F0} Castle early \u2014 build your FORTRESS!", | |
| "\u{1F4D0} Bishops love Long Diagonals for attack lines!", | |
| "\u{1F5FC} Rooks need Open Files to be powerful!", | |
| "\u{1F91D} Coordinate your pieces! Teamwork = power!", | |
| "\u26A0\uFE0F Count attackers vs defenders before capturing!", | |
| "\u{1F9E0} Checks, Captures, Threats \u2014 hero scanning order!", | |
| "\u{1F4A1} A Passed Pawn dreams of becoming a Queen!" | |
| ]; | |
| var ENERGY_LEVELS = [ | |
| { percent: 0, label: "No Power", color: "#475569", emoji: "\u{1F50B}" }, | |
| { percent: 20, label: "Warming Up", color: "#ef4444", emoji: "\u{1F534}" }, | |
| { percent: 40, label: "Getting Strong", color: "#f97316", emoji: "\u{1F7E0}" }, | |
| { percent: 60, label: "Power Rising!", color: "#eab308", emoji: "\u{1F7E1}" }, | |
| { percent: 80, label: "Almost Ready!", color: "#22c55e", emoji: "\u{1F7E2}" }, | |
| { percent: 100, label: "FULL POWER!", color: "#8b5cf6", emoji: "\u26A1" } | |
| ]; | |
| function getEnergyLevel(percent) { | |
| let result = ENERGY_LEVELS[0]; | |
| for (const level of ENERGY_LEVELS) { | |
| if (percent >= level.percent) | |
| result = level; | |
| } | |
| return result; | |
| } | |
| __name(getEnergyLevel, "getEnergyLevel"); | |
| var TIMER_QUOTES_HERO = [ | |
| "Heroes take their time! Think carefully! \u{1F9B8}", | |
| "With great power comes great thinking! \u{1F9E0}", | |
| "A hero's best weapon is patience! \u23F0", | |
| "Power up your brain before your move! \u26A1", | |
| "Scan the board like a true hero! \u{1F440}", | |
| "Heroes don't rush into danger! \u{1F6E1}\uFE0F", | |
| "Take a deep breath and focus your powers! \u{1F4AB}", | |
| "Every great hero thinks before they act! \u{1F31F}" | |
| ]; | |
| var _sizeMap = { | |
| heroEmoji: "characterEmoji", | |
| speechBubble: "speechBubble", | |
| powerName: "title", | |
| powerDescription: "subtitle", | |
| tip: "tip", | |
| timer: "timer", | |
| button: "button", | |
| energyLabel: "smallLabel" | |
| }; | |
| var FONT_SIZES = new Proxy({}, { | |
| get(_, key) { | |
| const mapped = _sizeMap[key]; | |
| return mapped ? getKidsFontSizes()[mapped] : ""; | |
| } | |
| }); | |
| var FONT_FAMILY = KIDS_FONT_FAMILY; | |
| var TIMING = { | |
| powerAutoSeconds: 10, | |
| tipAutoSeconds: 8, | |
| speechDuration: 4e3, | |
| transformDuration: 3e3 | |
| }; | |
| // src/game-play-for-kids-3/state.ts | |
| var _HeroState = class _HeroState { | |
| constructor() { | |
| // Character | |
| this.selectedHero = HERO_CHARACTERS[0]; | |
| this.characterPickerShown = false; | |
| // Power state | |
| this.currentPower = 0; | |
| this.powerTimerInterval = null; | |
| this.powerTimeRemaining = 0; | |
| this.powersActivated = 0; | |
| this.energyPercent = 0; | |
| // Tip state | |
| this.currentTip = 0; | |
| this.tipInterval = null; | |
| this.tipSecondsLeft = TIMING.tipAutoSeconds; | |
| // Timer state | |
| this.timerInterval = null; | |
| this.timerElapsed = 0; | |
| // Move tracking | |
| this.lastActiveMove = ""; | |
| // Speech | |
| this.speechTimeout = null; | |
| // Stats | |
| this.totalTransformations = 0; | |
| this.totalPowersUsed = 0; | |
| } | |
| loadHero() { | |
| try { | |
| const saved = localStorage.getItem(STORAGE_KEY); | |
| if (saved) { | |
| const data = JSON.parse(saved); | |
| const hero = HERO_CHARACTERS.find((h) => h.id === data.id); | |
| if (hero) { | |
| this.selectedHero = hero; | |
| this.characterPickerShown = true; | |
| this.totalTransformations = data.transforms || 0; | |
| this.totalPowersUsed = data.powers || 0; | |
| } | |
| } | |
| } catch { | |
| } | |
| } | |
| saveHero(heroId) { | |
| try { | |
| const hero = HERO_CHARACTERS.find((h) => h.id === heroId); | |
| if (hero) { | |
| this.selectedHero = hero; | |
| this.characterPickerShown = true; | |
| } | |
| this.persistStats(); | |
| } catch { | |
| } | |
| } | |
| persistStats() { | |
| try { | |
| localStorage.setItem( | |
| STORAGE_KEY, | |
| JSON.stringify({ | |
| id: this.selectedHero.id, | |
| transforms: this.totalTransformations, | |
| powers: this.totalPowersUsed | |
| }) | |
| ); | |
| } catch { | |
| } | |
| } | |
| getRandomQuote() { | |
| return randomItem(TIMER_QUOTES_HERO); | |
| } | |
| getBattleCry() { | |
| return randomItem(this.selectedHero.battleCries); | |
| } | |
| getPowerUpLine() { | |
| return randomItem(this.selectedHero.powerUpLines); | |
| } | |
| getTransformLine() { | |
| return randomItem(this.selectedHero.transformLines); | |
| } | |
| getDefeatLine() { | |
| return randomItem(this.selectedHero.defeatLines); | |
| } | |
| getQuickMoveWarning() { | |
| return randomItem(this.selectedHero.quickMoveWarnings); | |
| } | |
| nextPower() { | |
| this.currentPower++; | |
| if (this.currentPower >= SUPERPOWERS.length) { | |
| this.currentPower = SUPERPOWERS.length; | |
| } | |
| } | |
| prevPower() { | |
| if (this.currentPower > 0) { | |
| this.currentPower--; | |
| } | |
| } | |
| nextTip() { | |
| this.currentTip = (this.currentTip + 1) % HERO_TIPS.length; | |
| } | |
| prevTip() { | |
| this.currentTip = (this.currentTip - 1 + HERO_TIPS.length) % HERO_TIPS.length; | |
| } | |
| resetPowers() { | |
| this.currentPower = 0; | |
| this.powerTimeRemaining = 0; | |
| this.powersActivated = 0; | |
| this.energyPercent = 0; | |
| } | |
| activatePower() { | |
| this.powersActivated++; | |
| this.totalPowersUsed++; | |
| this.energyPercent = Math.min( | |
| 100, | |
| Math.round(this.powersActivated / SUPERPOWERS.length * 100) | |
| ); | |
| } | |
| isFullyPowered() { | |
| return this.powersActivated >= SUPERPOWERS.length; | |
| } | |
| resetTimer() { | |
| this.timerElapsed = 0; | |
| } | |
| resetTipTimer() { | |
| this.tipSecondsLeft = TIMING.tipAutoSeconds; | |
| } | |
| }; | |
| __name(_HeroState, "HeroState"); | |
| var HeroState = _HeroState; | |
| var state = new HeroState(); | |
| // src/game-play-for-kids-3/timers.ts | |
| function showHeroSpeech(msg) { | |
| const speechEl = document.getElementById("vmk3-hero-speech"); | |
| if (speechEl) { | |
| speechEl.textContent = `${state.selectedHero.emoji} ${msg}`; | |
| speechEl.classList.add("visible"); | |
| if (state.speechTimeout) | |
| clearTimeout(state.speechTimeout); | |
| state.speechTimeout = setTimeout(() => { | |
| speechEl.classList.remove("visible"); | |
| }, TIMING.speechDuration); | |
| } | |
| } | |
| __name(showHeroSpeech, "showHeroSpeech"); | |
| function startTimer(tDiv) { | |
| if (state.timerInterval) | |
| clearInterval(state.timerInterval); | |
| state.timerElapsed = 0; | |
| tDiv.textContent = "\u26A1 0s"; | |
| tDiv.style.color = "#ef4444"; | |
| state.timerInterval = setInterval(() => { | |
| state.timerElapsed++; | |
| tDiv.textContent = `\u26A1 ${state.timerElapsed}s`; | |
| if (state.timerElapsed < 5) { | |
| tDiv.style.color = "#ef4444"; | |
| } else if (state.timerElapsed < 10) { | |
| tDiv.style.color = "#fde047"; | |
| } else { | |
| tDiv.style.color = "#10b981"; | |
| } | |
| if (state.timerElapsed === 10) { | |
| showHeroSpeech("Your hero brain is CHARGING! Keep thinking! \u{1F9E0}\u26A1"); | |
| } else if (state.timerElapsed === 20) { | |
| showHeroSpeech("INCREDIBLE focus! A true hero! \u{1F9B8}\u{1F31F}"); | |
| } | |
| }, 1e3); | |
| } | |
| __name(startTimer, "startTimer"); | |
| function startPowerTimer() { | |
| if (state.powerTimerInterval) | |
| clearInterval(state.powerTimerInterval); | |
| const power = SUPERPOWERS[state.currentPower]; | |
| if (!power) | |
| return; | |
| state.powerTimeRemaining = power.timer; | |
| showHeroSpeech(power.heroShout); | |
| const activateBtn = document.getElementById("vmk3-activate-btn"); | |
| if (activateBtn) { | |
| activateBtn.textContent = `Power Up! \u26A1 (${state.powerTimeRemaining}s)`; | |
| } | |
| state.powerTimerInterval = setInterval(() => { | |
| state.powerTimeRemaining--; | |
| if (activateBtn) { | |
| activateBtn.textContent = `Power Up! \u26A1 (${state.powerTimeRemaining}s)`; | |
| } | |
| if (state.powerTimeRemaining <= 0) { | |
| clearInterval(state.powerTimerInterval); | |
| state.powerTimerInterval = null; | |
| state.activatePower(); | |
| state.currentPower++; | |
| updateEnergyBar(); | |
| if (state.currentPower >= SUPERPOWERS.length) { | |
| showTransformation(); | |
| } else { | |
| updatePowerDisplay(); | |
| startPowerTimer(); | |
| } | |
| } | |
| }, 1e3); | |
| } | |
| __name(startPowerTimer, "startPowerTimer"); | |
| function stopPowerTimer() { | |
| if (state.powerTimerInterval) { | |
| clearInterval(state.powerTimerInterval); | |
| state.powerTimerInterval = null; | |
| } | |
| } | |
| __name(stopPowerTimer, "stopPowerTimer"); | |
| function showTransformation() { | |
| state.totalTransformations++; | |
| state.persistStats(); | |
| const powerText = document.getElementById("vmk3-power-text"); | |
| const activateBtn = document.getElementById("vmk3-activate-btn"); | |
| const hero = state.selectedHero; | |
| if (powerText) { | |
| powerText.innerHTML = `<div style="text-align:center;"> | |
| <div style="font-size:44px; margin-bottom:6px; animation: vmk3-transform 1s ease-out;"> | |
| ${hero.transformedEmoji} | |
| </div> | |
| <div style="font-size:22px; color:#fef3c7; font-weight:800; text-shadow:2px 2px 4px rgba(0,0,0,0.5);"> | |
| FULL POWER! | |
| </div> | |
| <div style="font-size:16px; color:#86efac; margin-top:4px; font-weight:700;"> | |
| Make your SUPER MOVE! \u{1F4A5} | |
| </div> | |
| <div style="font-size:13px; color:#94a3b8; margin-top:6px; font-weight:600;"> | |
| Transformations: ${state.totalTransformations} \u26A1 | |
| </div> | |
| </div>`; | |
| } | |
| if (activateBtn) | |
| activateBtn.style.display = "none"; | |
| stopPowerTimer(); | |
| showHeroSpeech(state.getTransformLine()); | |
| const heroEmoji = document.querySelector( | |
| "#vmk3-custom-sidebar .vmk3-hero-hover" | |
| ); | |
| if (heroEmoji) { | |
| heroEmoji.textContent = hero.transformedEmoji; | |
| heroEmoji.style.animation = "vmk3-transform 1s ease-out, vmk3-hero-glow 2s ease-in-out infinite"; | |
| } | |
| } | |
| __name(showTransformation, "showTransformation"); | |
| function updatePowerDisplay() { | |
| const power = SUPERPOWERS[state.currentPower]; | |
| if (!power) | |
| return; | |
| const powerText = document.getElementById("vmk3-power-text"); | |
| if (powerText) { | |
| const energyDots = "\u26A1".repeat(power.energyCost) + "\u25CB".repeat(3 - power.energyCost); | |
| powerText.innerHTML = ` | |
| <div style="font-size:30px; margin-bottom:4px;">${power.emoji}</div> | |
| <div style="font-size:${FONT_SIZES.powerName}; font-weight:800; color:${power.color}; text-shadow:1px 1px 3px rgba(0,0,0,0.5);"> | |
| ${power.name} | |
| </div> | |
| <div style="font-size:${FONT_SIZES.powerDescription}; color:#cbd5e1; margin-top:4px; font-weight:600; line-height:1.4;">${power.description}</div> | |
| <div style="font-size:12px; color:#64748b; margin-top:4px; font-weight:600;">Energy: ${energyDots}</div> | |
| `; | |
| } | |
| const powerNumEl = document.getElementById("vmk3-power-number"); | |
| if (powerNumEl) { | |
| powerNumEl.textContent = `Power ${state.currentPower + 1}/${SUPERPOWERS.length}`; | |
| } | |
| const backBtn = document.getElementById("vmk3-back-btn"); | |
| if (backBtn) { | |
| backBtn.style.visibility = state.currentPower === 0 ? "hidden" : "visible"; | |
| } | |
| } | |
| __name(updatePowerDisplay, "updatePowerDisplay"); | |
| function updateEnergyBar() { | |
| const energyFill = document.getElementById("vmk3-energy-fill"); | |
| const energyLabel = document.getElementById("vmk3-energy-label"); | |
| if (energyFill) { | |
| energyFill.style.width = `${state.energyPercent}%`; | |
| const level = getEnergyLevel(state.energyPercent); | |
| energyFill.style.background = `linear-gradient(90deg, ${level.color}, ${level.color}aa)`; | |
| } | |
| if (energyLabel) { | |
| const level = getEnergyLevel(state.energyPercent); | |
| energyLabel.textContent = `${level.emoji} ${state.energyPercent}% - ${level.label}`; | |
| energyLabel.style.color = level.color; | |
| } | |
| } | |
| __name(updateEnergyBar, "updateEnergyBar"); | |
| function startTipAutoAdvance() { | |
| if (state.tipInterval) | |
| clearInterval(state.tipInterval); | |
| state.tipSecondsLeft = TIMING.tipAutoSeconds; | |
| const tipTextEl = document.getElementById("vmk3-tip-text"); | |
| const nextBtn = document.getElementById("vmk3-tip-next-btn"); | |
| if (nextBtn) { | |
| nextBtn.textContent = `Next (${state.tipSecondsLeft}s) \u2192`; | |
| } | |
| state.tipInterval = setInterval(() => { | |
| state.tipSecondsLeft--; | |
| if (nextBtn) { | |
| nextBtn.textContent = `Next (${state.tipSecondsLeft}s) \u2192`; | |
| } | |
| if (state.tipSecondsLeft <= 0) { | |
| state.currentTip = (state.currentTip + 1) % HERO_TIPS.length; | |
| if (tipTextEl) { | |
| tipTextEl.textContent = HERO_TIPS[state.currentTip]; | |
| } | |
| state.tipSecondsLeft = TIMING.tipAutoSeconds; | |
| if (nextBtn) { | |
| nextBtn.textContent = `Next (${state.tipSecondsLeft}s) \u2192`; | |
| } | |
| } | |
| }, 1e3); | |
| } | |
| __name(startTipAutoAdvance, "startTipAutoAdvance"); | |
| // src/game-play-for-kids-3/ui.ts | |
| function injectStyles() { | |
| if (document.getElementById("vmk3-styles")) | |
| return; | |
| injectKidsFont("vmk3"); | |
| const style = document.createElement("style"); | |
| style.id = "vmk3-styles"; | |
| style.textContent = ` | |
| ${getKidsSpeechBubbleCSS("vmk3")} | |
| @keyframes vmk3-hero-float { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-6px); } | |
| } | |
| @keyframes vmk3-transform { | |
| 0% { transform: scale(0.3) rotate(-180deg); opacity: 0; } | |
| 50% { transform: scale(1.3) rotate(10deg); opacity: 1; } | |
| 100% { transform: scale(1) rotate(0deg); opacity: 1; } | |
| } | |
| @keyframes vmk3-hero-glow { | |
| 0%, 100% { filter: drop-shadow(0 0 5px rgba(255,215,0,0.3)); } | |
| 50% { filter: drop-shadow(0 0 15px rgba(255,215,0,0.8)); } | |
| } | |
| @keyframes vmk3-energy-pulse { | |
| 0%, 100% { box-shadow: 0 0 5px rgba(139, 92, 246, 0.3); } | |
| 50% { box-shadow: 0 0 15px rgba(139, 92, 246, 0.7); } | |
| } | |
| @keyframes vmk3-power-activate { | |
| 0% { opacity: 0; transform: scale(0.5) rotate(-10deg); } | |
| 60% { transform: scale(1.1) rotate(3deg); } | |
| 100% { opacity: 1; transform: scale(1) rotate(0deg); } | |
| } | |
| @keyframes vmk3-float-power { | |
| 0% { opacity: 1; transform: translateY(0) scale(1); } | |
| 100% { opacity: 0; transform: translateY(-50px) scale(1.5); } | |
| } | |
| @keyframes vmk3-energy-shimmer { | |
| 0% { background-position: -200% 0; } | |
| 100% { background-position: 200% 0; } | |
| } | |
| @keyframes vmk3-shake { | |
| 0%, 100% { transform: translateX(0); } | |
| 25% { transform: translateX(-3px); } | |
| 75% { transform: translateX(3px); } | |
| } | |
| .vmk3-hero-hover:hover { | |
| animation: vmk3-shake 0.4s ease-in-out !important; | |
| cursor: pointer; | |
| } | |
| .vmk3-btn { | |
| font-family: ${FONT_FAMILY}; | |
| letter-spacing: 0.5px; | |
| font-weight: 700; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| text-transform: uppercase; | |
| } | |
| .vmk3-btn:hover { | |
| transform: scale(1.08); | |
| filter: brightness(1.2); | |
| } | |
| .vmk3-power-card { | |
| animation: vmk3-power-activate 0.4s ease-out; | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| } | |
| __name(injectStyles, "injectStyles"); | |
| function createCharacterPicker() { | |
| const container = document.createElement("div"); | |
| container.id = "vmk3-character-picker"; | |
| container.style.cssText = ` | |
| width: 100%; margin-top: 16px; padding: 20px; | |
| background: linear-gradient(135deg, #0f0024 0%, #1a0a3e 50%, #2d1b69 100%); | |
| border-radius: 16px; box-shadow: 0 8px 32px rgba(0,0,0,0.5); | |
| border: 2px solid #7c3aed40; box-sizing: border-box; | |
| font-family: ${FONT_FAMILY}; | |
| text-align: center; | |
| `.replace(/\s+/g, " ").trim(); | |
| container.innerHTML = ` | |
| <div style="font-size: 26px; color: #fef3c7; font-weight: 800; margin-bottom: 4px; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); letter-spacing: 1px;"> | |
| \u{1F9B8} CHOOSE YOUR HERO! \u{1F9B8} | |
| </div> | |
| <div style="font-size: 14px; color: #c4b5fd; margin-bottom: 16px; font-weight: 600;"> | |
| Pick your superhero to train with! | |
| </div> | |
| <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;"> | |
| ${HERO_CHARACTERS.map( | |
| (h) => ` | |
| <div class="vmk3-hero-option" data-hero-id="${h.id}" style=" | |
| padding: 12px 8px; border-radius: 14px; cursor: pointer; | |
| background: ${h.bgGradient}; border: 3px solid transparent; | |
| transition: all 0.3s; text-align: center; | |
| " onmouseover="this.style.border='3px solid ${h.color}'; this.style.transform='scale(1.05)';" | |
| onmouseout="this.style.border='3px solid transparent'; this.style.transform='scale(1)';"> | |
| <div style="font-size: 40px; margin-bottom: 2px;">${h.emoji}</div> | |
| <div style="font-size: 14px; font-weight: 700; color: ${h.color};">${h.name}</div> | |
| <div style="font-size: 12px; color: #94a3b8; margin-top: 2px; font-weight: 600;">\u26A1 ${h.superpower}</div> | |
| </div> | |
| ` | |
| ).join("")} | |
| </div> | |
| `; | |
| setTimeout(() => { | |
| const options = container.querySelectorAll(".vmk3-hero-option"); | |
| options.forEach((opt) => { | |
| opt.addEventListener("click", () => { | |
| const heroId = opt.dataset.heroId; | |
| if (heroId) { | |
| state.saveHero(heroId); | |
| const picker = document.getElementById("vmk3-character-picker"); | |
| if (picker) | |
| picker.remove(); | |
| injectMainUI(); | |
| } | |
| }); | |
| }); | |
| }, 100); | |
| return container; | |
| } | |
| __name(createCharacterPicker, "createCharacterPicker"); | |
| function injectMainUI() { | |
| if (document.getElementById("vmk3-custom-sidebar")) | |
| return; | |
| const gameMeta = document.querySelector(".game__meta"); | |
| if (!gameMeta) | |
| return; | |
| const hero = state.selectedHero; | |
| const customDiv = document.createElement("div"); | |
| customDiv.id = "vmk3-custom-sidebar"; | |
| customDiv.style.cssText = ` | |
| width: 100%; margin-top: 16px; padding: 16px; | |
| background: ${hero.bgGradient}; | |
| border-radius: 16px; box-shadow: 0 8px 32px rgba(0,0,0,0.5); | |
| border: 2px solid ${hero.color}40; | |
| box-sizing: border-box; position: relative; overflow: visible; | |
| font-family: ${FONT_FAMILY}; | |
| `.replace(/\s+/g, " ").trim(); | |
| const heroSection = document.createElement("div"); | |
| heroSection.style.cssText = ` | |
| display: flex; align-items: center; gap: 10px; | |
| margin-bottom: 10px; | |
| `.replace(/\s+/g, " ").trim(); | |
| const heroEmoji = document.createElement("div"); | |
| heroEmoji.className = "vmk3-hero-hover"; | |
| heroEmoji.style.cssText = ` | |
| font-size: ${FONT_SIZES.heroEmoji}; cursor: pointer; | |
| animation: vmk3-hero-float 2.5s ease-in-out infinite; | |
| filter: drop-shadow(0 4px 8px rgba(0,0,0,0.3)); | |
| `.replace(/\s+/g, " ").trim(); | |
| heroEmoji.textContent = hero.emoji; | |
| heroEmoji.title = "Click to change hero!"; | |
| heroEmoji.onclick = () => { | |
| state.characterPickerShown = false; | |
| const sidebar = document.getElementById("vmk3-custom-sidebar"); | |
| if (sidebar) | |
| sidebar.remove(); | |
| const picker = createCharacterPicker(); | |
| const gm = document.querySelector(".game__meta"); | |
| if (gm) | |
| gm.parentNode?.insertBefore(picker, gm.nextSibling); | |
| }; | |
| const heroInfo = document.createElement("div"); | |
| heroInfo.style.cssText = "flex: 1;"; | |
| heroInfo.innerHTML = ` | |
| <div style="font-size: 17px; font-weight: 800; color: ${hero.color}; text-shadow: 1px 1px 3px rgba(0,0,0,0.5); letter-spacing: 0.5px;"> | |
| ${hero.name} | |
| </div> | |
| <div style="font-size: 13px; color: #94a3b8; font-weight: 700;"> | |
| \u26A1 ${hero.superpower} | |
| </div> | |
| <div style="font-size: 12px; color: #64748b; margin-top: 2px; font-weight: 600;"> | |
| Transformations: ${state.totalTransformations} | Powers: ${state.totalPowersUsed} | |
| </div> | |
| `; | |
| heroSection.appendChild(heroEmoji); | |
| heroSection.appendChild(heroInfo); | |
| const bubbleWrapper = document.createElement("div"); | |
| bubbleWrapper.className = "vmk3-bubble-wrapper"; | |
| const speechBubble = document.createElement("div"); | |
| speechBubble.id = "vmk3-hero-speech"; | |
| speechBubble.className = "vmk3-comic-bubble visible"; | |
| speechBubble.style.cssText = ` | |
| font-size: ${FONT_SIZES.speechBubble}; color: #e2e8f0; | |
| text-align: center; line-height: 1.5; font-weight: 600; | |
| text-shadow: 1px 1px 2px rgba(0,0,0,0.3); | |
| `.replace(/\s+/g, " ").trim(); | |
| speechBubble.textContent = `${hero.emoji} ${state.getBattleCry()}`; | |
| bubbleWrapper.appendChild(speechBubble); | |
| setTimeout( | |
| () => speechBubble.classList.remove("visible"), | |
| TIMING.speechDuration | |
| ); | |
| const energySection = document.createElement("div"); | |
| energySection.style.cssText = ` | |
| width: 100%; margin-bottom: 10px; | |
| `.replace(/\s+/g, " ").trim(); | |
| const energyLabel = document.createElement("div"); | |
| energyLabel.id = "vmk3-energy-label"; | |
| energyLabel.style.cssText = ` | |
| font-size: ${FONT_SIZES.energyLabel}; color: #475569; | |
| text-align: center; margin-bottom: 4px; | |
| font-weight: 700; | |
| `.replace(/\s+/g, " ").trim(); | |
| energyLabel.textContent = "\u{1F50B} 0% - No Power"; | |
| const energyBarOuter = document.createElement("div"); | |
| energyBarOuter.style.cssText = ` | |
| width: 100%; height: 12px; background: rgba(255,255,255,0.08); | |
| border-radius: 6px; overflow: hidden; | |
| border: 1px solid rgba(255,255,255,0.1); | |
| animation: vmk3-energy-pulse 3s ease-in-out infinite; | |
| `.replace(/\s+/g, " ").trim(); | |
| const energyFill = document.createElement("div"); | |
| energyFill.id = "vmk3-energy-fill"; | |
| energyFill.style.cssText = ` | |
| width: 0%; height: 100%; border-radius: 6px; | |
| background: linear-gradient(90deg, #475569, #47556999); | |
| transition: width 0.5s ease, background 0.5s ease; | |
| background-size: 200% 100%; | |
| animation: vmk3-energy-shimmer 2s linear infinite; | |
| `.replace(/\s+/g, " ").trim(); | |
| energyBarOuter.appendChild(energyFill); | |
| energySection.appendChild(energyLabel); | |
| energySection.appendChild(energyBarOuter); | |
| const timerDiv = document.createElement("div"); | |
| timerDiv.id = "vmk3-timer"; | |
| timerDiv.textContent = "\u26A1 0s"; | |
| timerDiv.style.cssText = ` | |
| width: 100%; margin-bottom: 10px; font-size: ${FONT_SIZES.timer}; | |
| font-weight: 800; color: #ef4444; text-align: center; | |
| padding: 6px; background: rgba(0,0,0,0.3); border-radius: 8px; | |
| box-sizing: border-box; | |
| `.replace(/\s+/g, " ").trim(); | |
| const powerSection = createPowerSection(); | |
| const tipsSection = createTipsSection(); | |
| const quoteDiv = document.createElement("div"); | |
| quoteDiv.id = "vmk3-quote"; | |
| quoteDiv.textContent = state.getRandomQuote(); | |
| quoteDiv.style.cssText = ` | |
| width: 100%; font-style: italic; font-size: 14px; | |
| color: #64748b; text-align: center; box-sizing: border-box; | |
| font-weight: 600; line-height: 1.4; | |
| `.replace(/\s+/g, " ").trim(); | |
| const effectsContainer = document.createElement("div"); | |
| effectsContainer.id = "vmk3-effects"; | |
| effectsContainer.style.cssText = ` | |
| position: absolute; top: 0; left: 0; width: 100%; height: 100%; | |
| pointer-events: none; overflow: hidden; z-index: 10; | |
| `.replace(/\s+/g, " ").trim(); | |
| customDiv.appendChild(effectsContainer); | |
| customDiv.appendChild(heroSection); | |
| customDiv.appendChild(bubbleWrapper); | |
| customDiv.appendChild(energySection); | |
| customDiv.appendChild(timerDiv); | |
| customDiv.appendChild(powerSection); | |
| customDiv.appendChild(tipsSection); | |
| customDiv.appendChild(quoteDiv); | |
| gameMeta.parentNode?.insertBefore(customDiv, gameMeta.nextSibling); | |
| startTimer(timerDiv); | |
| startTipAutoAdvance(); | |
| startPowerTimer(); | |
| } | |
| __name(injectMainUI, "injectMainUI"); | |
| function createPowerSection() { | |
| const power = SUPERPOWERS[state.currentPower]; | |
| const energyDots = "\u26A1".repeat(power.energyCost) + "\u25CB".repeat(3 - power.energyCost); | |
| const powerDiv = document.createElement("div"); | |
| powerDiv.id = "vmk3-power-section"; | |
| powerDiv.className = "vmk3-power-card"; | |
| powerDiv.style.cssText = ` | |
| width: 100%; margin-bottom: 12px; padding: 14px; | |
| background: rgba(15, 23, 42, 0.6); border-radius: 14px; | |
| border: 2px solid ${power.color}50; | |
| box-sizing: border-box; position: relative; | |
| `.replace(/\s+/g, " ").trim(); | |
| const powerHeader = document.createElement("div"); | |
| powerHeader.style.cssText = ` | |
| display: flex; justify-content: space-between; align-items: center; | |
| margin-bottom: 8px; padding-bottom: 6px; border-bottom: 1px solid rgba(255,255,255,0.1); | |
| `.replace(/\s+/g, " ").trim(); | |
| powerHeader.innerHTML = ` | |
| <div style="font-size:13px; color:#fbbf24; font-weight:800; letter-spacing:1px;">\u26A1 HERO POWERS</div> | |
| <div id="vmk3-power-number" style="font-size:12px; color:#64748b; font-weight:600;"> | |
| Power ${state.currentPower + 1}/${SUPERPOWERS.length} | |
| </div> | |
| `; | |
| const powerText = document.createElement("div"); | |
| powerText.id = "vmk3-power-text"; | |
| powerText.style.cssText = ` | |
| width: 100%; text-align: center; min-height: 70px; | |
| margin-bottom: 10px; | |
| `.replace(/\s+/g, " ").trim(); | |
| powerText.innerHTML = ` | |
| <div style="font-size:30px; margin-bottom:4px;">${power.emoji}</div> | |
| <div style="font-size:${FONT_SIZES.powerName}; font-weight:800; color:${power.color}; text-shadow:1px 1px 3px rgba(0,0,0,0.5); letter-spacing:0.5px;"> | |
| ${power.name} | |
| </div> | |
| <div style="font-size:${FONT_SIZES.powerDescription}; color:#cbd5e1; margin-top:4px; font-weight:600; line-height:1.4;"> | |
| ${power.description} | |
| </div> | |
| <div style="font-size:12px; color:#64748b; margin-top:4px; font-weight:600;">Energy: ${energyDots}</div> | |
| `; | |
| const btnContainer = document.createElement("div"); | |
| btnContainer.style.cssText = "width: 100%; display: flex; justify-content: space-between; gap: 10px;"; | |
| const backBtn = document.createElement("button"); | |
| backBtn.id = "vmk3-back-btn"; | |
| backBtn.textContent = "\u2190 BACK"; | |
| backBtn.className = "vmk3-btn"; | |
| backBtn.style.cssText = ` | |
| background: rgba(71, 85, 105, 0.5); color: #e2e8f0; | |
| font-size: ${FONT_SIZES.button}; visibility: hidden; | |
| border: 1px solid rgba(255,255,255,0.2); | |
| padding: 7px 12px; border-radius: 10px; | |
| font-family: ${FONT_FAMILY}; | |
| cursor: pointer; transition: all 0.2s; letter-spacing: 0.5px; | |
| `.replace(/\s+/g, " ").trim(); | |
| backBtn.onmouseover = () => { | |
| backBtn.style.transform = "scale(1.06)"; | |
| }; | |
| backBtn.onmouseout = () => { | |
| backBtn.style.transform = "scale(1)"; | |
| }; | |
| backBtn.onclick = () => { | |
| if (state.currentPower > 0) { | |
| state.currentPower--; | |
| updatePowerUI(); | |
| startPowerTimer(); | |
| } | |
| }; | |
| const activateBtn = document.createElement("button"); | |
| activateBtn.id = "vmk3-activate-btn"; | |
| activateBtn.textContent = "Power Up! \u26A1"; | |
| activateBtn.className = "vmk3-btn"; | |
| activateBtn.style.cssText = ` | |
| background: linear-gradient(135deg, ${power.color}, ${power.color}cc); | |
| color: #ffffff; font-size: ${FONT_SIZES.button}; | |
| border: 2px solid rgba(255,255,255,0.3); | |
| padding: 7px 16px; border-radius: 10px; | |
| font-family: ${FONT_FAMILY}; | |
| cursor: pointer; transition: all 0.2s; letter-spacing: 0.5px; | |
| box-shadow: 0 4px 12px ${power.color}40; | |
| `.replace(/\s+/g, " ").trim(); | |
| activateBtn.onmouseover = () => { | |
| activateBtn.style.transform = "scale(1.08)"; | |
| }; | |
| activateBtn.onmouseout = () => { | |
| activateBtn.style.transform = "scale(1)"; | |
| }; | |
| activateBtn.onclick = () => { | |
| state.activatePower(); | |
| state.currentPower++; | |
| state.persistStats(); | |
| backBtn.style.visibility = "visible"; | |
| spawnPowerEffect(); | |
| updateEnergyBar2(); | |
| showHeroSpeech(state.getPowerUpLine()); | |
| if (state.currentPower >= SUPERPOWERS.length) { | |
| state.totalTransformations++; | |
| state.persistStats(); | |
| const powerTextEl = document.getElementById("vmk3-power-text"); | |
| if (powerTextEl) { | |
| powerTextEl.innerHTML = `<div style="text-align:center;"> | |
| <div style="font-size:44px; margin-bottom:6px; animation: vmk3-transform 1s ease-out;"> | |
| ${state.selectedHero.transformedEmoji} | |
| </div> | |
| <div style="font-size:22px; color:#fef3c7; font-weight:800; text-shadow:2px 2px 4px rgba(0,0,0,0.5); letter-spacing:1px;"> | |
| FULL POWER! | |
| </div> | |
| <div style="font-size:16px; color:#86efac; margin-top:4px; font-weight:700;"> | |
| Make your SUPER MOVE! \u{1F4A5} | |
| </div> | |
| </div>`; | |
| } | |
| activateBtn.style.display = "none"; | |
| stopPowerTimer(); | |
| showHeroSpeech(state.getTransformLine()); | |
| const heroEmoji = document.querySelector( | |
| "#vmk3-custom-sidebar .vmk3-hero-hover" | |
| ); | |
| if (heroEmoji) { | |
| heroEmoji.textContent = state.selectedHero.transformedEmoji; | |
| heroEmoji.style.animation = "vmk3-transform 1s ease-out, vmk3-hero-glow 2s ease-in-out infinite"; | |
| } | |
| } else { | |
| updatePowerUI(); | |
| startPowerTimer(); | |
| } | |
| }; | |
| btnContainer.appendChild(backBtn); | |
| btnContainer.appendChild(activateBtn); | |
| powerDiv.appendChild(powerHeader); | |
| powerDiv.appendChild(powerText); | |
| powerDiv.appendChild(btnContainer); | |
| return powerDiv; | |
| } | |
| __name(createPowerSection, "createPowerSection"); | |
| function updatePowerUI() { | |
| const power = SUPERPOWERS[state.currentPower]; | |
| if (!power) | |
| return; | |
| const energyDots = "\u26A1".repeat(power.energyCost) + "\u25CB".repeat(3 - power.energyCost); | |
| const powerText = document.getElementById("vmk3-power-text"); | |
| if (powerText) { | |
| powerText.innerHTML = ` | |
| <div style="font-size:30px; margin-bottom:4px;">${power.emoji}</div> | |
| <div style="font-size:${FONT_SIZES.powerName}; font-weight:800; color:${power.color}; text-shadow:1px 1px 3px rgba(0,0,0,0.5); letter-spacing:0.5px;"> | |
| ${power.name} | |
| </div> | |
| <div style="font-size:${FONT_SIZES.powerDescription}; color:#cbd5e1; margin-top:4px; font-weight:600; line-height:1.4;"> | |
| ${power.description} | |
| </div> | |
| <div style="font-size:12px; color:#64748b; margin-top:4px; font-weight:600;">Energy: ${energyDots}</div> | |
| `; | |
| } | |
| const powerNumEl = document.getElementById("vmk3-power-number"); | |
| if (powerNumEl) { | |
| powerNumEl.textContent = `Power ${state.currentPower + 1}/${SUPERPOWERS.length}`; | |
| } | |
| const activateBtn = document.getElementById("vmk3-activate-btn"); | |
| if (activateBtn) { | |
| activateBtn.style.background = `linear-gradient(135deg, ${power.color}, ${power.color}cc)`; | |
| activateBtn.style.boxShadow = `0 4px 12px ${power.color}40`; | |
| } | |
| const powerSection = document.getElementById("vmk3-power-section"); | |
| if (powerSection) { | |
| powerSection.style.borderColor = `${power.color}50`; | |
| } | |
| const backBtn = document.getElementById("vmk3-back-btn"); | |
| if (backBtn) { | |
| backBtn.style.visibility = state.currentPower === 0 ? "hidden" : "visible"; | |
| } | |
| } | |
| __name(updatePowerUI, "updatePowerUI"); | |
| function updateEnergyBar2() { | |
| const energyFill = document.getElementById("vmk3-energy-fill"); | |
| const energyLabel = document.getElementById("vmk3-energy-label"); | |
| if (energyFill) { | |
| energyFill.style.width = `${state.energyPercent}%`; | |
| const level = getEnergyLevel(state.energyPercent); | |
| energyFill.style.background = `linear-gradient(90deg, ${level.color}, ${level.color}aa)`; | |
| } | |
| if (energyLabel) { | |
| const level = getEnergyLevel(state.energyPercent); | |
| energyLabel.textContent = `${level.emoji} ${state.energyPercent}% - ${level.label}`; | |
| energyLabel.style.color = level.color; | |
| } | |
| } | |
| __name(updateEnergyBar2, "updateEnergyBar"); | |
| function createTipsSection() { | |
| const tipsDiv = document.createElement("div"); | |
| tipsDiv.id = "vmk3-tips"; | |
| tipsDiv.style.cssText = ` | |
| width: 100%; margin-bottom: 8px; padding: 8px; | |
| background: rgba(100, 116, 139, 0.1); border: 1px solid rgba(255,255,255,0.08); | |
| border-radius: 8px; color: #94a3b8; box-sizing: border-box; opacity: 0.85; | |
| `.replace(/\s+/g, " ").trim(); | |
| const tipTextDiv = document.createElement("div"); | |
| tipTextDiv.style.cssText = ` | |
| width: 100%; font-size: ${FONT_SIZES.tip}; line-height: 1.4; | |
| min-height: 36px; margin-bottom: 6px; text-align: center; | |
| font-weight: 600; | |
| `.replace(/\s+/g, " ").trim(); | |
| const tipText = document.createElement("span"); | |
| tipText.id = "vmk3-tip-text"; | |
| tipText.textContent = HERO_TIPS[state.currentTip]; | |
| tipTextDiv.appendChild(tipText); | |
| const btnContainer = document.createElement("div"); | |
| btnContainer.style.cssText = "width:100%; display:flex; justify-content:space-between; gap:8px; height:22px;"; | |
| const backBtn = document.createElement("button"); | |
| backBtn.textContent = "\u2190 BACK"; | |
| backBtn.style.cssText = ` | |
| background: #475569; border: none; color: #e2e8f0; | |
| padding: 3px 8px; border-radius: 4px; cursor: pointer; | |
| font-size: 11px; transition: all 0.2s; | |
| font-family: ${FONT_FAMILY}; font-weight: 700; | |
| `.replace(/\s+/g, " ").trim(); | |
| backBtn.onmouseover = () => { | |
| backBtn.style.transform = "scale(1.05)"; | |
| }; | |
| backBtn.onmouseout = () => { | |
| backBtn.style.transform = "scale(1)"; | |
| }; | |
| backBtn.onclick = () => { | |
| state.prevTip(); | |
| tipText.textContent = HERO_TIPS[state.currentTip]; | |
| startTipAutoAdvance(); | |
| }; | |
| const nextBtn = document.createElement("button"); | |
| nextBtn.id = "vmk3-tip-next-btn"; | |
| nextBtn.textContent = `NEXT (${TIMING.tipAutoSeconds}s) \u2192`; | |
| nextBtn.style.cssText = ` | |
| background: #475569; border: none; color: #e2e8f0; | |
| padding: 3px 8px; border-radius: 4px; cursor: pointer; | |
| font-size: 11px; transition: all 0.2s; | |
| font-family: ${FONT_FAMILY}; font-weight: 700; | |
| `.replace(/\s+/g, " ").trim(); | |
| nextBtn.onmouseover = () => { | |
| nextBtn.style.transform = "scale(1.05)"; | |
| }; | |
| nextBtn.onmouseout = () => { | |
| nextBtn.style.transform = "scale(1)"; | |
| }; | |
| nextBtn.onclick = () => { | |
| state.nextTip(); | |
| tipText.textContent = HERO_TIPS[state.currentTip]; | |
| startTipAutoAdvance(); | |
| }; | |
| btnContainer.appendChild(backBtn); | |
| btnContainer.appendChild(nextBtn); | |
| tipsDiv.appendChild(tipTextDiv); | |
| tipsDiv.appendChild(btnContainer); | |
| return tipsDiv; | |
| } | |
| __name(createTipsSection, "createTipsSection"); | |
| function spawnPowerEffect() { | |
| const container = document.getElementById("vmk3-effects"); | |
| if (!container) | |
| return; | |
| const emojis = ["\u26A1", "\u{1F4A5}", "\u2728", "\u{1F31F}", "\u{1F4AB}", "\u{1F525}", "\u2B50", "\u{1F9B8}"]; | |
| for (let i = 0; i < 3; i++) { | |
| const emoji = randomItem(emojis); | |
| const el = document.createElement("div"); | |
| el.textContent = emoji; | |
| el.style.cssText = ` | |
| position: absolute; font-size: 20px; pointer-events: none; | |
| left: ${10 + Math.random() * 80}%; top: ${30 + Math.random() * 40}%; | |
| animation: vmk3-float-power 1.5s ease-out forwards; | |
| animation-delay: ${i * 0.15}s; | |
| `.replace(/\s+/g, " ").trim(); | |
| container.appendChild(el); | |
| setTimeout(() => el.remove(), 2e3); | |
| } | |
| } | |
| __name(spawnPowerEffect, "spawnPowerEffect"); | |
| function checkMoveChange() { | |
| const activeMove = document.querySelector("kwdb.a1t"); | |
| if (!activeMove) | |
| return false; | |
| const moveAttr = activeMove.getAttribute("p") || activeMove.className || ""; | |
| const moveText = activeMove.textContent?.trim() || ""; | |
| const currentMove = moveAttr + moveText; | |
| if (currentMove && currentMove !== state.lastActiveMove) { | |
| state.lastActiveMove = currentMove; | |
| return true; | |
| } | |
| return false; | |
| } | |
| __name(checkMoveChange, "checkMoveChange"); | |
| function resetPowerState() { | |
| state.resetPowers(); | |
| state.currentPower = 0; | |
| updatePowerUI(); | |
| updateEnergyBar2(); | |
| const activateBtn = document.getElementById("vmk3-activate-btn"); | |
| if (activateBtn) | |
| activateBtn.style.display = ""; | |
| const heroEmoji = document.querySelector( | |
| "#vmk3-custom-sidebar .vmk3-hero-hover" | |
| ); | |
| if (heroEmoji) { | |
| heroEmoji.textContent = state.selectedHero.emoji; | |
| heroEmoji.style.animation = "vmk3-hero-float 2.5s ease-in-out infinite"; | |
| } | |
| showHeroSpeech(state.getBattleCry()); | |
| startPowerTimer(); | |
| const tDiv = document.getElementById("vmk3-timer"); | |
| if (tDiv) { | |
| tDiv.style.color = "#ef4444"; | |
| startTimer(tDiv); | |
| } | |
| if (state.timerElapsed < 3) { | |
| setTimeout(() => { | |
| showHeroSpeech(state.getQuickMoveWarning()); | |
| }, 1e3); | |
| } | |
| const quoteDiv = document.getElementById("vmk3-quote"); | |
| if (quoteDiv) | |
| quoteDiv.textContent = state.getRandomQuote(); | |
| } | |
| __name(resetPowerState, "resetPowerState"); | |
| function injectCustomSidebarDiv() { | |
| if (document.getElementById("vmk3-custom-sidebar") || document.getElementById("vmk3-character-picker")) { | |
| if (checkMoveChange()) { | |
| resetPowerState(); | |
| } | |
| return; | |
| } | |
| const gameMeta = document.querySelector(".game__meta"); | |
| if (!gameMeta) | |
| return; | |
| injectStyles(); | |
| state.loadHero(); | |
| if (!state.characterPickerShown) { | |
| const picker = createCharacterPicker(); | |
| gameMeta.parentNode?.insertBefore(picker, gameMeta.nextSibling); | |
| } else { | |
| injectMainUI(); | |
| } | |
| } | |
| __name(injectCustomSidebarDiv, "injectCustomSidebarDiv"); | |
| // src/game-play-for-kids-3/index.ts | |
| function init() { | |
| const observer = new MutationObserver(() => { | |
| injectCustomSidebarDiv(); | |
| }); | |
| observer.observe(document.documentElement, { | |
| childList: true, | |
| subtree: true | |
| }); | |
| let retryCount = 0; | |
| const maxRetries = 50; | |
| const retryInterval = setInterval(() => { | |
| retryCount++; | |
| injectCustomSidebarDiv(); | |
| if (document.getElementById("vmk3-custom-sidebar") || document.getElementById("vmk3-character-picker") || retryCount >= maxRetries) { | |
| clearInterval(retryInterval); | |
| } | |
| }, 200); | |
| } | |
| __name(init, "init"); | |
| init(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment