diff --git a/frontend/.gitignore b/frontend/.gitignore index a547bf3..8a38d8f 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -11,6 +11,7 @@ node_modules dist dist-ssr *.local +*.vite # Editor directories and files .vscode/* diff --git a/frontend/public/game/cards/a1/033-charmander.webp b/frontend/public/game/cards/a1/033-charmander.webp new file mode 100644 index 0000000..00956ef Binary files /dev/null and b/frontend/public/game/cards/a1/033-charmander.webp differ diff --git a/frontend/public/game/cards/a1/034-charmeleon.webp b/frontend/public/game/cards/a1/034-charmeleon.webp new file mode 100644 index 0000000..f819575 Binary files /dev/null and b/frontend/public/game/cards/a1/034-charmeleon.webp differ diff --git a/frontend/public/game/cards/a1/035-charizard.webp b/frontend/public/game/cards/a1/035-charizard.webp new file mode 100644 index 0000000..0feb63a Binary files /dev/null and b/frontend/public/game/cards/a1/035-charizard.webp differ diff --git a/frontend/public/game/cards/a1/037-vulpix.webp b/frontend/public/game/cards/a1/037-vulpix.webp new file mode 100644 index 0000000..5e4b646 Binary files /dev/null and b/frontend/public/game/cards/a1/037-vulpix.webp differ diff --git a/frontend/public/game/cards/a1/039-growlithe.webp b/frontend/public/game/cards/a1/039-growlithe.webp new file mode 100644 index 0000000..7159bd8 Binary files /dev/null and b/frontend/public/game/cards/a1/039-growlithe.webp differ diff --git a/frontend/public/game/cards/a1/040-arcanine.webp b/frontend/public/game/cards/a1/040-arcanine.webp new file mode 100644 index 0000000..2110878 Binary files /dev/null and b/frontend/public/game/cards/a1/040-arcanine.webp differ diff --git a/frontend/public/game/cards/a1/042-ponyta.webp b/frontend/public/game/cards/a1/042-ponyta.webp new file mode 100644 index 0000000..b8be5b1 Binary files /dev/null and b/frontend/public/game/cards/a1/042-ponyta.webp differ diff --git a/frontend/public/game/cards/a1/043-rapidash.webp b/frontend/public/game/cards/a1/043-rapidash.webp new file mode 100644 index 0000000..48082cd Binary files /dev/null and b/frontend/public/game/cards/a1/043-rapidash.webp differ diff --git a/frontend/public/game/cards/a1/044-magmar.webp b/frontend/public/game/cards/a1/044-magmar.webp new file mode 100644 index 0000000..76f38e8 Binary files /dev/null and b/frontend/public/game/cards/a1/044-magmar.webp differ diff --git a/frontend/public/game/cards/a1/094-pikachu.webp b/frontend/public/game/cards/a1/094-pikachu.webp new file mode 100644 index 0000000..1aeea6d Binary files /dev/null and b/frontend/public/game/cards/a1/094-pikachu.webp differ diff --git a/frontend/public/game/cards/a1/095-raichu.webp b/frontend/public/game/cards/a1/095-raichu.webp new file mode 100644 index 0000000..c86aae3 Binary files /dev/null and b/frontend/public/game/cards/a1/095-raichu.webp differ diff --git a/frontend/public/game/cards/a1/097-magnemite.webp b/frontend/public/game/cards/a1/097-magnemite.webp new file mode 100644 index 0000000..916e795 Binary files /dev/null and b/frontend/public/game/cards/a1/097-magnemite.webp differ diff --git a/frontend/public/game/cards/a1/098-magneton.webp b/frontend/public/game/cards/a1/098-magneton.webp new file mode 100644 index 0000000..09dd11a Binary files /dev/null and b/frontend/public/game/cards/a1/098-magneton.webp differ diff --git a/frontend/public/game/cards/a1/099-voltorb.webp b/frontend/public/game/cards/a1/099-voltorb.webp new file mode 100644 index 0000000..3654516 Binary files /dev/null and b/frontend/public/game/cards/a1/099-voltorb.webp differ diff --git a/frontend/public/game/cards/a1/100-electrode.webp b/frontend/public/game/cards/a1/100-electrode.webp new file mode 100644 index 0000000..5d0afb4 Binary files /dev/null and b/frontend/public/game/cards/a1/100-electrode.webp differ diff --git a/frontend/public/game/cards/a1/101-electabuzz.webp b/frontend/public/game/cards/a1/101-electabuzz.webp new file mode 100644 index 0000000..6c98cd1 Binary files /dev/null and b/frontend/public/game/cards/a1/101-electabuzz.webp differ diff --git a/frontend/public/game/cards/a1/105-blitzle.webp b/frontend/public/game/cards/a1/105-blitzle.webp new file mode 100644 index 0000000..04fd55e Binary files /dev/null and b/frontend/public/game/cards/a1/105-blitzle.webp differ diff --git a/frontend/public/game/cards/a1/106-zebstrika.webp b/frontend/public/game/cards/a1/106-zebstrika.webp new file mode 100644 index 0000000..e7230bd Binary files /dev/null and b/frontend/public/game/cards/a1/106-zebstrika.webp differ diff --git a/frontend/public/game/cards/a1/216-helix-fossil.webp b/frontend/public/game/cards/a1/216-helix-fossil.webp new file mode 100644 index 0000000..97652c6 Binary files /dev/null and b/frontend/public/game/cards/a1/216-helix-fossil.webp differ diff --git a/frontend/public/game/cards/a1/217-dome-fossil.webp b/frontend/public/game/cards/a1/217-dome-fossil.webp new file mode 100644 index 0000000..0f2c3a7 Binary files /dev/null and b/frontend/public/game/cards/a1/217-dome-fossil.webp differ diff --git a/frontend/public/game/cards/a1/218-old-amber.webp b/frontend/public/game/cards/a1/218-old-amber.webp new file mode 100644 index 0000000..1fd329b Binary files /dev/null and b/frontend/public/game/cards/a1/218-old-amber.webp differ diff --git a/frontend/public/game/cards/a1/221-blaine.webp b/frontend/public/game/cards/a1/221-blaine.webp new file mode 100644 index 0000000..942da74 Binary files /dev/null and b/frontend/public/game/cards/a1/221-blaine.webp differ diff --git a/frontend/public/game/cards/a1/224-brock.webp b/frontend/public/game/cards/a1/224-brock.webp new file mode 100644 index 0000000..defae3b Binary files /dev/null and b/frontend/public/game/cards/a1/224-brock.webp differ diff --git a/frontend/public/game/cards/a1/226-lt-surge.webp b/frontend/public/game/cards/a1/226-lt-surge.webp new file mode 100644 index 0000000..a844d06 Binary files /dev/null and b/frontend/public/game/cards/a1/226-lt-surge.webp differ diff --git a/frontend/public/game/cards/basic/fire.webp b/frontend/public/game/cards/basic/fire.webp new file mode 100644 index 0000000..516d448 Binary files /dev/null and b/frontend/public/game/cards/basic/fire.webp differ diff --git a/frontend/public/game/cards/basic/grass.webp b/frontend/public/game/cards/basic/grass.webp new file mode 100644 index 0000000..633213c Binary files /dev/null and b/frontend/public/game/cards/basic/grass.webp differ diff --git a/frontend/public/game/cards/basic/lightning.webp b/frontend/public/game/cards/basic/lightning.webp new file mode 100644 index 0000000..9db45df Binary files /dev/null and b/frontend/public/game/cards/basic/lightning.webp differ diff --git a/frontend/public/game/cards/basic/psychic.webp b/frontend/public/game/cards/basic/psychic.webp new file mode 100644 index 0000000..03e5c67 Binary files /dev/null and b/frontend/public/game/cards/basic/psychic.webp differ diff --git a/frontend/public/game/cards/basic/water.webp b/frontend/public/game/cards/basic/water.webp new file mode 100644 index 0000000..30e79f6 Binary files /dev/null and b/frontend/public/game/cards/basic/water.webp differ diff --git a/frontend/public/game/cards/card_back.webp b/frontend/public/game/cards/card_back.webp new file mode 100644 index 0000000..f1684ac Binary files /dev/null and b/frontend/public/game/cards/card_back.webp differ diff --git a/frontend/src/components/game/PhaserGame.vue b/frontend/src/components/game/PhaserGame.vue index 8608fe4..32aaa13 100644 --- a/frontend/src/components/game/PhaserGame.vue +++ b/frontend/src/components/game/PhaserGame.vue @@ -8,7 +8,7 @@ */ import { ref, onMounted, onUnmounted, shallowRef } from 'vue' -import { createGame } from '@/game' +import { createGame, scenes } from '@/game' import type Phaser from 'phaser' /** @@ -44,7 +44,7 @@ function initGame(): void { try { // Create the Phaser game instance - game.value = createGame(container.value) + game.value = createGame(container.value, scenes) // Listen for Phaser ready event (emitted when game is fully initialized) // Using PHASER_READY_EVENT constant to avoid runtime Phaser dependency diff --git a/frontend/src/data/demoCards.json b/frontend/src/data/demoCards.json new file mode 100644 index 0000000..14f091a --- /dev/null +++ b/frontend/src/data/demoCards.json @@ -0,0 +1,700 @@ +{ + "a1-094-pikachu": { + "id": "a1-094-pikachu", + "name": "Pikachu", + "card_type": "pokemon", + "hp": 60, + "pokemon_type": "lightning", + "stage": "basic", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Gnaw", + "cost": [ + "lightning" + ], + "damage": 20, + "damage_display": "20" + } + ], + "weakness": { + "energy_type": "fighting", + "value": 20 + }, + "illustrator": "Mitsuhiro Arita", + "image_path": "a1/094-pikachu.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/094-pikachu.webp" + }, + "a1-095-raichu": { + "id": "a1-095-raichu", + "name": "Raichu", + "card_type": "pokemon", + "hp": 100, + "pokemon_type": "lightning", + "stage": "stage_1", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "rare", + "evolves_from": "Pikachu", + "attacks": [ + { + "name": "Thunderbolt", + "cost": [ + "lightning", + "lightning", + "lightning" + ], + "damage": 140, + "damage_display": "140", + "effect_description": "Discard all Energy from this Pok\u00e9mon." + } + ], + "weakness": { + "energy_type": "fighting", + "value": 20 + }, + "illustrator": "AKIRA EGAWA", + "image_path": "a1/095-raichu.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/095-raichu.webp" + }, + "a1-097-magnemite": { + "id": "a1-097-magnemite", + "name": "Magnemite", + "card_type": "pokemon", + "hp": 60, + "pokemon_type": "lightning", + "stage": "basic", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Lightning Ball", + "cost": [ + "lightning" + ], + "damage": 20, + "damage_display": "20" + } + ], + "weakness": { + "energy_type": "fighting", + "value": 20 + }, + "illustrator": "sowsow", + "image_path": "a1/097-magnemite.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/097-magnemite.webp" + }, + "a1-098-magneton": { + "id": "a1-098-magneton", + "name": "Magneton", + "card_type": "pokemon", + "hp": 80, + "pokemon_type": "lightning", + "stage": "stage_1", + "variant": "normal", + "retreat_cost": 2, + "set_id": "a1", + "rarity": "rare", + "evolves_from": "Magnemite", + "attacks": [ + { + "name": "Spinning Attack", + "cost": [ + "lightning", + "colorless", + "colorless", + "colorless" + ], + "damage": 60, + "damage_display": "60" + } + ], + "abilities": [ + { + "name": "Volt Charge", + "effect_id": "unimplemented", + "effect_description": "Once during your turn, you may take a Lightning Energy from your Energy Zone and attach it to this Pok\u00e9mon." + } + ], + "weakness": { + "energy_type": "fighting", + "value": 20 + }, + "illustrator": "kirisAki", + "image_path": "a1/098-magneton.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/098-magneton.webp" + }, + "a1-099-voltorb": { + "id": "a1-099-voltorb", + "name": "Voltorb", + "card_type": "pokemon", + "hp": 60, + "pokemon_type": "lightning", + "stage": "basic", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Tackle", + "cost": [ + "lightning" + ], + "damage": 20, + "damage_display": "20" + } + ], + "weakness": { + "energy_type": "fighting", + "value": 20 + }, + "illustrator": "SATOSHI NAKAI", + "image_path": "a1/099-voltorb.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/099-voltorb.webp" + }, + "a1-100-electrode": { + "id": "a1-100-electrode", + "name": "Electrode", + "card_type": "pokemon", + "hp": 80, + "pokemon_type": "lightning", + "stage": "stage_1", + "variant": "normal", + "retreat_cost": 0, + "set_id": "a1", + "rarity": "uncommon", + "evolves_from": "Voltorb", + "attacks": [ + { + "name": "Electro Ball", + "cost": [ + "lightning", + "lightning" + ], + "damage": 70, + "damage_display": "70" + } + ], + "weakness": { + "energy_type": "fighting", + "value": 20 + }, + "illustrator": "Asako Ito", + "image_path": "a1/100-electrode.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/100-electrode.webp" + }, + "a1-101-electabuzz": { + "id": "a1-101-electabuzz", + "name": "Electabuzz", + "card_type": "pokemon", + "hp": 70, + "pokemon_type": "lightning", + "stage": "basic", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Thunder Punch", + "cost": [ + "lightning", + "lightning" + ], + "damage": 40, + "damage_display": "40x", + "effect_description": "Flip a coin. If heads, this attack does 40 more damage. If tails, this Pok\u00e9mon also does 20 damage to itself.", + "effect_params": { + "damage_modifier": "x" + } + } + ], + "weakness": { + "energy_type": "fighting", + "value": 20 + }, + "illustrator": "Ryuta Fuse", + "image_path": "a1/101-electabuzz.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/101-electabuzz.webp" + }, + "a1-105-blitzle": { + "id": "a1-105-blitzle", + "name": "Blitzle", + "card_type": "pokemon", + "hp": 60, + "pokemon_type": "lightning", + "stage": "basic", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Zap Kick", + "cost": [ + "lightning" + ], + "damage": 20, + "damage_display": "20" + } + ], + "weakness": { + "energy_type": "fighting", + "value": 20 + }, + "illustrator": "Shin Nagasawa", + "image_path": "a1/105-blitzle.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/105-blitzle.webp" + }, + "a1-106-zebstrika": { + "id": "a1-106-zebstrika", + "name": "Zebstrika", + "card_type": "pokemon", + "hp": 90, + "pokemon_type": "lightning", + "stage": "stage_1", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "uncommon", + "evolves_from": "Blitzle", + "attacks": [ + { + "name": "Thunder Spear", + "cost": [ + "lightning" + ], + "damage": 0, + "effect_description": "This attack does 30 damage to 1 of your opponent\u2019s Pok\u00e9mon." + } + ], + "weakness": { + "energy_type": "fighting", + "value": 20 + }, + "illustrator": "Misa Tsutsui", + "image_path": "a1/106-zebstrika.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/106-zebstrika.webp" + }, + "a1-033-charmander": { + "id": "a1-033-charmander", + "name": "Charmander", + "card_type": "pokemon", + "hp": 60, + "pokemon_type": "fire", + "stage": "basic", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Ember", + "cost": [ + "fire" + ], + "damage": 30, + "damage_display": "30", + "effect_description": "Discard a Fire Energy from this Pok\u00e9mon." + } + ], + "weakness": { + "energy_type": "water", + "value": 20 + }, + "illustrator": "Teeziro", + "image_path": "a1/033-charmander.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/033-charmander.webp" + }, + "a1-034-charmeleon": { + "id": "a1-034-charmeleon", + "name": "Charmeleon", + "card_type": "pokemon", + "hp": 90, + "pokemon_type": "fire", + "stage": "stage_1", + "variant": "normal", + "retreat_cost": 2, + "set_id": "a1", + "rarity": "uncommon", + "evolves_from": "Charmander", + "attacks": [ + { + "name": "Fire Claws", + "cost": [ + "fire", + "colorless", + "colorless" + ], + "damage": 60, + "damage_display": "60" + } + ], + "weakness": { + "energy_type": "water", + "value": 20 + }, + "illustrator": "kantaro", + "image_path": "a1/034-charmeleon.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/034-charmeleon.webp" + }, + "a1-035-charizard": { + "id": "a1-035-charizard", + "name": "Charizard", + "card_type": "pokemon", + "hp": 150, + "pokemon_type": "fire", + "stage": "stage_2", + "variant": "normal", + "retreat_cost": 2, + "set_id": "a1", + "rarity": "rare", + "evolves_from": "Charmeleon", + "attacks": [ + { + "name": "Fire Spin", + "cost": [ + "fire", + "fire", + "colorless", + "colorless" + ], + "damage": 150, + "damage_display": "150", + "effect_description": "Discard 2 Fire Energy from this Pok\u00e9mon." + } + ], + "weakness": { + "energy_type": "water", + "value": 20 + }, + "illustrator": "takuyoa", + "image_path": "a1/035-charizard.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/035-charizard.webp" + }, + "a1-039-growlithe": { + "id": "a1-039-growlithe", + "name": "Growlithe", + "card_type": "pokemon", + "hp": 70, + "pokemon_type": "fire", + "stage": "basic", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Bite", + "cost": [ + "colorless", + "colorless" + ], + "damage": 20, + "damage_display": "20" + } + ], + "weakness": { + "energy_type": "water", + "value": 20 + }, + "illustrator": "Mizue", + "image_path": "a1/039-growlithe.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/039-growlithe.webp" + }, + "a1-040-arcanine": { + "id": "a1-040-arcanine", + "name": "Arcanine", + "card_type": "pokemon", + "hp": 130, + "pokemon_type": "fire", + "stage": "stage_1", + "variant": "normal", + "retreat_cost": 2, + "set_id": "a1", + "rarity": "rare", + "evolves_from": "Growlithe", + "attacks": [ + { + "name": "Heat Tackle", + "cost": [ + "fire", + "fire", + "colorless" + ], + "damage": 100, + "damage_display": "100", + "effect_description": "This Pok\u00e9mon also does 20 damage to itself." + } + ], + "weakness": { + "energy_type": "water", + "value": 20 + }, + "illustrator": "kodama", + "image_path": "a1/040-arcanine.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/040-arcanine.webp" + }, + "a1-042-ponyta": { + "id": "a1-042-ponyta", + "name": "Ponyta", + "card_type": "pokemon", + "hp": 60, + "pokemon_type": "fire", + "stage": "basic", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Flare", + "cost": [ + "fire" + ], + "damage": 20, + "damage_display": "20" + } + ], + "weakness": { + "energy_type": "water", + "value": 20 + }, + "illustrator": "Uta", + "image_path": "a1/042-ponyta.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/042-ponyta.webp" + }, + "a1-043-rapidash": { + "id": "a1-043-rapidash", + "name": "Rapidash", + "card_type": "pokemon", + "hp": 100, + "pokemon_type": "fire", + "stage": "stage_1", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "uncommon", + "evolves_from": "Ponyta", + "attacks": [ + { + "name": "Fire Mane", + "cost": [ + "fire" + ], + "damage": 40, + "damage_display": "40" + } + ], + "weakness": { + "energy_type": "water", + "value": 20 + }, + "illustrator": "Misa Tsutsui", + "image_path": "a1/043-rapidash.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/043-rapidash.webp" + }, + "a1-037-vulpix": { + "id": "a1-037-vulpix", + "name": "Vulpix", + "card_type": "pokemon", + "hp": 50, + "pokemon_type": "fire", + "stage": "basic", + "variant": "normal", + "retreat_cost": 1, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Tail Whip", + "cost": [ + "colorless" + ], + "damage": 0, + "effect_description": "Flip a coin. If heads, the Defending Pok\u00e9mon can\u2019t attack during your opponent\u2019s next turn." + } + ], + "weakness": { + "energy_type": "water", + "value": 20 + }, + "illustrator": "Toshinao Aoki", + "image_path": "a1/037-vulpix.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/037-vulpix.webp" + }, + "a1-044-magmar": { + "id": "a1-044-magmar", + "name": "Magmar", + "card_type": "pokemon", + "hp": 80, + "pokemon_type": "fire", + "stage": "basic", + "variant": "normal", + "retreat_cost": 2, + "set_id": "a1", + "rarity": "common", + "attacks": [ + { + "name": "Magma Punch", + "cost": [ + "fire", + "fire" + ], + "damage": 50, + "damage_display": "50" + } + ], + "weakness": { + "energy_type": "water", + "value": 20 + }, + "illustrator": "Ryuta Fuse", + "image_path": "a1/044-magmar.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/044-magmar.webp" + }, + "a1-221-blaine": { + "id": "a1-221-blaine", + "name": "Blaine", + "card_type": "trainer", + "trainer_type": "supporter", + "set_id": "a1", + "rarity": "uncommon", + "effect_description": "During this turn, attacks used by your Ninetales , Rapidash , or Magmar do +30 damage to your opponent\u2019s Active Pok\u00e9mon. You may play only 1 Supporter card during your turn.", + "illustrator": "GOSSAN", + "image_path": "a1/221-blaine.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/221-blaine.webp" + }, + "a1-224-brock": { + "id": "a1-224-brock", + "name": "Brock", + "card_type": "trainer", + "trainer_type": "supporter", + "set_id": "a1", + "rarity": "uncommon", + "effect_description": "Take a Fighting Energy from your Energy Zone and attach it to Golem or Onix . You may play only 1 Supporter card during your turn.", + "illustrator": "Taira Akitsu", + "image_path": "a1/224-brock.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/224-brock.webp" + }, + "a1-216-helix-fossil": { + "id": "a1-216-helix-fossil", + "name": "Helix Fossil", + "card_type": "trainer", + "trainer_type": "item", + "set_id": "a1", + "rarity": "common", + "effect_description": "Play this card as if it were a 40 HP Basic Colorless Pok\u00e9mon. At any time during your turn, you may discard this card from play. This card can\u2019t retreat. You may play any number of Item cards during your turn.", + "illustrator": "Toyste Beach", + "image_path": "a1/216-helix-fossil.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/216-helix-fossil.webp" + }, + "a1-217-dome-fossil": { + "id": "a1-217-dome-fossil", + "name": "Dome Fossil", + "card_type": "trainer", + "trainer_type": "item", + "set_id": "a1", + "rarity": "common", + "effect_description": "Play this card as if it were a 40 HP Basic Colorless Pok\u00e9mon. At any time during your turn, you may discard this card from play. This card can\u2019t retreat. You may play any number of Item cards during your turn.", + "illustrator": "Toyste Beach", + "image_path": "a1/217-dome-fossil.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/217-dome-fossil.webp" + }, + "a1-218-old-amber": { + "id": "a1-218-old-amber", + "name": "Old Amber", + "card_type": "trainer", + "trainer_type": "item", + "set_id": "a1", + "rarity": "common", + "effect_description": "Play this card as if it were a 40 HP Basic Colorless Pok\u00e9mon. At any time during your turn, you may discard this card from play. This card can\u2019t retreat. You may play any number of Item cards during your turn.", + "illustrator": "Toyste Beach", + "image_path": "a1/218-old-amber.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/218-old-amber.webp" + }, + "a1-226-lt-surge": { + "id": "a1-226-lt-surge", + "name": "Lt. Surge", + "card_type": "trainer", + "trainer_type": "supporter", + "set_id": "a1", + "rarity": "uncommon", + "effect_description": "Move all Lightning Energy from your Benched Pok\u00e9mon to your Raichu , Electrode , or Electabuzz in the Active Spot. You may play only 1 Supporter card during your turn.", + "illustrator": "nagimiso", + "image_path": "a1/226-lt-surge.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/a1/226-lt-surge.webp" + }, + "energy-basic-lightning": { + "id": "energy-basic-lightning", + "name": "Lightning Energy", + "card_type": "energy", + "energy_type": "lightning", + "energy_provides": [ + "lightning" + ], + "rarity": "common", + "set_id": "basic", + "image_path": "basic/lightning.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/basic/lightning.webp" + }, + "energy-basic-fire": { + "id": "energy-basic-fire", + "name": "Fire Energy", + "card_type": "energy", + "energy_type": "fire", + "energy_provides": [ + "fire" + ], + "rarity": "common", + "set_id": "basic", + "image_path": "basic/fire.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/basic/fire.webp" + }, + "energy-basic-water": { + "id": "energy-basic-water", + "name": "Water Energy", + "card_type": "energy", + "energy_type": "water", + "energy_provides": [ + "water" + ], + "rarity": "common", + "set_id": "basic", + "image_path": "basic/water.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/basic/water.webp" + }, + "energy-basic-grass": { + "id": "energy-basic-grass", + "name": "Grass Energy", + "card_type": "energy", + "energy_type": "grass", + "energy_provides": [ + "grass" + ], + "rarity": "common", + "set_id": "basic", + "image_path": "basic/grass.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/basic/grass.webp" + }, + "energy-basic-psychic": { + "id": "energy-basic-psychic", + "name": "Psychic Energy", + "card_type": "energy", + "energy_type": "psychic", + "energy_provides": [ + "psychic" + ], + "rarity": "common", + "set_id": "basic", + "image_path": "basic/psychic.webp", + "image_url": "https://mantipocket.s3.us-east-1.amazonaws.com/card-images/basic/psychic.webp" + } +} diff --git a/frontend/src/data/demoCards.ts b/frontend/src/data/demoCards.ts new file mode 100644 index 0000000..bada272 --- /dev/null +++ b/frontend/src/data/demoCards.ts @@ -0,0 +1,75 @@ +/** + * Demo card data for F3 Phaser integration testing. + * + * Contains real card definitions from the backend, bundled statically + * for the demo page. Includes: + * - Lightning starter deck Pokemon (Pikachu line, Magnemite line, etc.) + * - Fire starter deck Pokemon (Charmander line, Growlithe line, etc.) + * - Trainer cards (supporters, items) + * - Basic energy cards + * + * Card images are served from /public/game/cards/ (copied from backend). + */ + +import type { CardDefinition } from '@/types/game' + +import demoCardsJson from './demoCards.json' + +/** + * All demo card definitions, keyed by card ID. + */ +export const DEMO_CARD_DEFINITIONS: Record = + demoCardsJson as Record + +/** + * Get a demo card definition by ID. + * + * @param id - The card ID (e.g., "a1-094-pikachu") + * @returns The card definition, or undefined if not found + */ +export function getDemoCard(id: string): CardDefinition | undefined { + return DEMO_CARD_DEFINITIONS[id] +} + +/** + * Get all demo card IDs. + * + * @returns Array of all card IDs in the demo set + */ +export function getDemoCardIds(): string[] { + return Object.keys(DEMO_CARD_DEFINITIONS) +} + +/** + * Get demo cards filtered by type. + * + * @param cardType - The card type to filter by + * @returns Array of matching card definitions + */ +export function getDemoCardsByType( + cardType: 'pokemon' | 'trainer' | 'energy' +): CardDefinition[] { + return Object.values(DEMO_CARD_DEFINITIONS).filter( + (card) => card.card_type === cardType + ) +} + +/** + * Get demo Pokemon cards filtered by energy type. + * + * @param energyType - The Pokemon type to filter by + * @returns Array of matching Pokemon card definitions + */ +export function getDemoPokemonByType( + energyType: 'lightning' | 'fire' | 'water' | 'grass' | 'psychic' +): CardDefinition[] { + return Object.values(DEMO_CARD_DEFINITIONS).filter( + (card) => card.card_type === 'pokemon' && card.pokemon_type === energyType + ) +} + +// Pre-filtered card lists for easy access +export const LIGHTNING_POKEMON = getDemoPokemonByType('lightning') +export const FIRE_POKEMON = getDemoPokemonByType('fire') +export const TRAINER_CARDS = getDemoCardsByType('trainer') +export const ENERGY_CARDS = getDemoCardsByType('energy') diff --git a/frontend/src/game/assets/loader.ts b/frontend/src/game/assets/loader.ts index 17ddf68..b8f6b1f 100644 --- a/frontend/src/game/assets/loader.ts +++ b/frontend/src/game/assets/loader.ts @@ -133,43 +133,42 @@ function loadSingleAsset(scene: Phaser.Scene, asset: AssetDefinition): void { // ============================================================================= /** - * Card image URL pattern. - * Format: /game/cards/{setId}/{cardNumber}.png - */ -const CARD_IMAGE_PATTERN = 'cards/{setId}/{cardNumber}.png' - -/** - * Build the URL for a card image. + * Build the URL for a card image from its image_path. * - * @param setId - The card set identifier (e.g., 'base', 'jungle') - * @param cardNumber - The card number within the set + * Card definitions include an `image_path` field like "a1/094-pikachu.webp" + * or "basic/lightning.webp". This function prepends the base cards path. + * + * @param imagePath - The image path from card definition * @returns Full URL path to the card image */ -export function getCardImagePath(setId: string, cardNumber: string): string { - return CARD_IMAGE_PATTERN.replace('{setId}', setId).replace( - '{cardNumber}', - cardNumber - ) +export function getCardImageUrl(imagePath: string): string { + return `${ASSET_BASE_URL}/cards/${imagePath}` } /** - * Lazily load a card image when needed. + * Legacy function for backwards compatibility. + * @deprecated Use getCardImageUrl with image_path instead + */ +export function getCardImagePath(setId: string, cardNumber: string): string { + return `cards/${setId}/${cardNumber}.webp` +} + +/** + * Lazily load a card image from its image_path. * - * This function loads a specific card image on demand, rather than - * preloading all card images upfront. If the image fails to load, + * This function loads a specific card image on demand using the + * image_path from the card definition. If the image fails to load, * it will fall back to the placeholder image. * * @param scene - The Phaser scene to load the image into * @param cardId - Unique identifier for the card (used as texture key) - * @param setId - The card set identifier - * @param cardNumber - The card number within the set + * @param imagePath - The image_path from card definition (e.g., "a1/094-pikachu.webp") * @returns Promise that resolves when the image is loaded (or fallback is used) */ -export async function loadCardImage( +export async function loadCardImageFromPath( scene: Phaser.Scene, cardId: string, - setId: string, - cardNumber: string + imagePath: string ): Promise { // Check if already loaded if (scene.textures.exists(cardId)) { @@ -177,8 +176,7 @@ export async function loadCardImage( } return new Promise((resolve) => { - const imagePath = getCardImagePath(setId, cardNumber) - const fullUrl = `${ASSET_BASE_URL}/${imagePath}` + const fullUrl = getCardImageUrl(imagePath) // Set up success handler scene.load.once(`filecomplete-image-${cardId}`, () => { @@ -199,6 +197,27 @@ export async function loadCardImage( }) } +/** + * Lazily load a card image when needed. + * + * @deprecated Use loadCardImageFromPath with image_path instead + * + * @param scene - The Phaser scene to load the image into + * @param cardId - Unique identifier for the card (used as texture key) + * @param setId - The card set identifier + * @param cardNumber - The card number within the set + * @returns Promise that resolves when the image is loaded (or fallback is used) + */ +export async function loadCardImage( + scene: Phaser.Scene, + cardId: string, + setId: string, + cardNumber: string +): Promise { + const imagePath = `${setId}/${cardNumber}.webp` + return loadCardImageFromPath(scene, cardId, imagePath) +} + /** * Load multiple card images in parallel. * diff --git a/frontend/src/game/assets/manifest.ts b/frontend/src/game/assets/manifest.ts index c439c0d..b393508 100644 --- a/frontend/src/game/assets/manifest.ts +++ b/frontend/src/game/assets/manifest.ts @@ -86,13 +86,13 @@ export const ASSET_MANIFEST: AssetManifest = { { key: 'card_back', type: 'image', - path: 'cards/card_back.png', + path: 'cards/card_back.webp', required: true, }, { key: 'placeholder_card', type: 'image', - path: 'cards/placeholder_card.png', + path: 'cards/card_back.webp', // Use card back as placeholder for now required: true, }, { diff --git a/frontend/src/game/objects/Card.ts b/frontend/src/game/objects/Card.ts index fbd1df6..dc71fd3 100644 --- a/frontend/src/game/objects/Card.ts +++ b/frontend/src/game/objects/Card.ts @@ -31,8 +31,8 @@ import { CARD_SIZES } from '@/types/phaser' import { gameBridge } from '../bridge' import { DamageCounter } from './DamageCounter' import { + loadCardImageFromPath, loadCardImage, - getCardTextureKey, createPlaceholderTexture, } from '../assets/loader' import { PLACEHOLDER_KEYS } from '../assets/manifest' @@ -434,6 +434,9 @@ export class Card extends Phaser.GameObjects.Container { /** * Load and display the card face image. + * + * Uses image_path from card definition if available (preferred), + * otherwise falls back to constructing path from set_id and set_number. */ private async loadCardFace(): Promise { if (!this.cardDefinition || this.isLoading) return @@ -450,16 +453,26 @@ export class Card extends Phaser.GameObjects.Container { this.showLoadingIndicator() try { - // Load the card image - const setId = this.cardDefinition.set_id || 'base' - const cardNumber = this.cardDefinition.set_number?.toString() || '001' + let loadedKey: string - const loadedKey = await loadCardImage( - this.scene, - textureKey, - setId, - cardNumber - ) + // Prefer image_path from card definition (new format) + if (this.cardDefinition.image_path) { + loadedKey = await loadCardImageFromPath( + this.scene, + textureKey, + this.cardDefinition.image_path + ) + } else { + // Fallback to legacy set_id/set_number construction + const setId = this.cardDefinition.set_id || 'base' + const cardNumber = this.cardDefinition.set_number?.toString() || '001' + loadedKey = await loadCardImage( + this.scene, + textureKey, + setId, + cardNumber + ) + } // Display the loaded image this.displayCardSprite(loadedKey) diff --git a/frontend/src/pages/DemoPage.vue b/frontend/src/pages/DemoPage.vue new file mode 100644 index 0000000..32cd29b --- /dev/null +++ b/frontend/src/pages/DemoPage.vue @@ -0,0 +1,461 @@ + + + diff --git a/frontend/src/pages/GamePage.vue b/frontend/src/pages/GamePage.vue index c757cad..63331ae 100644 --- a/frontend/src/pages/GamePage.vue +++ b/frontend/src/pages/GamePage.vue @@ -250,8 +250,18 @@ onUnmounted(() => { stroke-linecap="round" stroke-linejoin="round" > - - + + @@ -268,7 +278,9 @@ onUnmounted(() => { data-testid="loading-overlay" >
-

Connecting to game...

+

+ Connecting to game... +

@@ -286,19 +298,48 @@ onUnmounted(() => { >