Merge remote-tracking branch 'origin/cleo' into raph_combat

raph_combat
Raphael LACOTE 3 days ago
commit f5fde68b14

@ -17,7 +17,6 @@
"phpstan/phpdoc-parser": "^1.14",
"symfony/asset": "6.1.*",
"symfony/console": "6.1.*",
"symfony/doctrine-messenger": "6.1.*",
"symfony/dotenv": "6.1.*",
"symfony/expression-language": "6.1.*",
"symfony/flex": "^2",
@ -32,7 +31,7 @@
"symfony/process": "6.1.*",
"symfony/property-access": "6.1.*",
"symfony/property-info": "6.1.*",
"symfony/runtime": "6.1.*",
"symfony/runtime": "^6.3.2",
"symfony/security-bundle": "6.1.*",
"symfony/serializer": "6.1.*",
"symfony/string": "6.1.*",

183
composer.lock generated

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d0596df364527b4a2e7e00a9d7213e87",
"content-hash": "a904471bc7e1136f2c1810044504fcc9",
"packages": [
{
"name": "doctrine/cache",
@ -2558,78 +2558,6 @@
],
"time": "2023-01-10T18:53:01+00:00"
},
{
"name": "symfony/doctrine-messenger",
"version": "v6.1.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/doctrine-messenger.git",
"reference": "e4b775d2dadcc801d2404c701555c2638690811e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/e4b775d2dadcc801d2404c701555c2638690811e",
"reference": "e4b775d2dadcc801d2404c701555c2638690811e",
"shasum": ""
},
"require": {
"doctrine/dbal": "^2.13|^3.0",
"php": ">=8.1",
"symfony/messenger": "^5.4|^6.0",
"symfony/service-contracts": "^1.1|^2|^3"
},
"conflict": {
"doctrine/persistence": "<1.3"
},
"require-dev": {
"doctrine/persistence": "^1.3|^2|^3",
"symfony/property-access": "^5.4|^6.0",
"symfony/serializer": "^5.4|^6.0"
},
"type": "symfony-messenger-bridge",
"autoload": {
"psr-4": {
"Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Doctrine Messenger Bridge",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/doctrine-messenger/tree/v6.1.11"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-01-01T08:36:55+00:00"
},
{
"name": "symfony/dotenv",
"version": "v6.1.11",
@ -3945,92 +3873,6 @@
],
"time": "2023-01-10T18:53:01+00:00"
},
{
"name": "symfony/messenger",
"version": "v6.1.11",
"source": {
"type": "git",
"url": "https://github.com/symfony/messenger.git",
"reference": "e3b7323b1d59fc77f5870735809bf39ce1596aee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/messenger/zipball/e3b7323b1d59fc77f5870735809bf39ce1596aee",
"reference": "e3b7323b1d59fc77f5870735809bf39ce1596aee",
"shasum": ""
},
"require": {
"php": ">=8.1",
"psr/log": "^1|^2|^3"
},
"conflict": {
"symfony/event-dispatcher": "<5.4",
"symfony/event-dispatcher-contracts": "<2",
"symfony/framework-bundle": "<5.4",
"symfony/http-kernel": "<5.4",
"symfony/serializer": "<5.4"
},
"require-dev": {
"psr/cache": "^1.0|^2.0|^3.0",
"symfony/console": "^5.4|^6.0",
"symfony/dependency-injection": "^5.4|^6.0",
"symfony/event-dispatcher": "^5.4|^6.0",
"symfony/http-kernel": "^5.4|^6.0",
"symfony/process": "^5.4|^6.0",
"symfony/property-access": "^5.4|^6.0",
"symfony/routing": "^5.4|^6.0",
"symfony/serializer": "^5.4|^6.0",
"symfony/service-contracts": "^1.1|^2|^3",
"symfony/stopwatch": "^5.4|^6.0",
"symfony/validator": "^5.4|^6.0"
},
"suggest": {
"enqueue/messenger-adapter": "For using the php-enqueue library as a transport."
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Messenger\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Samuel Roze",
"email": "samuel.roze@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Helps applications send and receive messages to/from other applications or via message queues",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/messenger/tree/v6.1.11"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-01-20T17:44:30+00:00"
},
{
"name": "symfony/mime",
"version": "v6.1.11",
@ -5293,16 +5135,16 @@
},
{
"name": "symfony/runtime",
"version": "v6.1.11",
"version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/runtime.git",
"reference": "717cb91d66893a27a4607f227e28eb74ff007fde"
"reference": "832c3ce3b810509815050434ccb7ead68d06395b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/runtime/zipball/717cb91d66893a27a4607f227e28eb74ff007fde",
"reference": "717cb91d66893a27a4607f227e28eb74ff007fde",
"url": "https://api.github.com/repos/symfony/runtime/zipball/832c3ce3b810509815050434ccb7ead68d06395b",
"reference": "832c3ce3b810509815050434ccb7ead68d06395b",
"shasum": ""
},
"require": {
@ -5314,10 +5156,10 @@
},
"require-dev": {
"composer/composer": "^1.0.2|^2.0",
"symfony/console": "^5.4|^6.0",
"symfony/dotenv": "^5.4|^6.0",
"symfony/http-foundation": "^5.4|^6.0",
"symfony/http-kernel": "^5.4|^6.0"
"symfony/console": "^5.4.9|^6.0.9|^7.0",
"symfony/dotenv": "^5.4|^6.0|^7.0",
"symfony/http-foundation": "^5.4|^6.0|^7.0",
"symfony/http-kernel": "^5.4|^6.0|^7.0"
},
"type": "composer-plugin",
"extra": {
@ -5348,8 +5190,11 @@
],
"description": "Enables decoupling PHP applications from global state",
"homepage": "https://symfony.com",
"keywords": [
"runtime"
],
"support": {
"source": "https://github.com/symfony/runtime/tree/v6.1.11"
"source": "https://github.com/symfony/runtime/tree/v6.4.22"
},
"funding": [
{
@ -5365,7 +5210,7 @@
"type": "tidelift"
}
],
"time": "2023-01-20T17:44:30+00:00"
"time": "2025-05-07T21:15:03+00:00"
},
{
"name": "symfony/security-bundle",

@ -1,29 +0,0 @@
framework:
messenger:
failure_transport: failed
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
use_notify: true
check_delayed_interval: 60000
retry_strategy:
max_retries: 3
multiplier: 2
failed: 'doctrine://default?queue_name=failed'
# sync: 'sync://'
default_bus: messenger.bus.default
buses:
messenger.bus.default: []
routing:
Symfony\Component\Mailer\Messenger\SendEmailMessage: async
Symfony\Component\Notifier\Message\ChatMessage: async
Symfony\Component\Notifier\Message\SmsMessage: async
# Route your messages to the transports
# 'App\Message\YourMessage': async

@ -0,0 +1,48 @@
création entity :
php bin/console make:entity Emoji
Réinstaller orm + doctrine_bundle :
composer require doctrine/orm doctrine/doctrine-bundle
Recompiler l'autoload (utile si ça persiste)
- composer dump-autoload
Vérifie ensuite que lentité est bien reconnue :
- php bin/console doctrine:mapping:info
'''Found 1 mapped entity:
[OK] App\Entity\Emoji'''
php bin/console make:migration
php bin/console doctrine:migrations:migrate
-------
Récréation BDD quand pb migrations :
php bin/console doctrine:database:drop --force
php bin/console doctrine:database:create
php bin/console doctrine:migrations:migrate
-------
Solution : Forcer la mise à jour de phpstan/phpdoc-parser
- composer require phpstan/phpdoc-parser:^1.26
Cree la BDD :
''' Modification .env (pour retirer URL_BDD)
Ajout .env.local (pour ajouter URL_BDD)'''
- symfony console doctrine:database:create
ok j'ai creer ma BDD avec symfony console doctrine:database:create comment j'applique mon entity dans la BDD que j'avais créer précedement avec symfony console

@ -0,0 +1,248 @@
body {
background-color: #314e57;
font-family: 'Georgia', serif;
text-align: center;
}
h1 {
font-size: 3rem;
margin-bottom: 30px;
text-shadow: 2px 2px 4px #000;
color: #f8b435;
}
.emoji-container {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 30px;
margin-bottom: 10rem;
}
.emoji-card {
background: #f2e6c9;
width: 180px;
height: 220px;
border: 4px solid #000;
border-radius: 12px;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
padding: 20px;
text-align: center;
font-family: 'Georgia', serif;
position: relative;
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
}
.emoji-card:hover {
transform: translateY(-8px) scale(1.03);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
z-index: 2;
}
/* Animation brillance pour rareté légendaire */
@keyframes shine {
0% { background-position: 0px; }
100% { background-position: 177px; }
}
.emoji-card.gold {
position: relative;
z-index: 1;
}
.emoji-card.gold::before {
content: '';
position: absolute;
top: 0;
left: 1%;
width: 98%;
height: 100%;
background: linear-gradient(
120deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.5) 50%,
rgba(255, 255, 255, 0) 100%
);
animation: shine 5s infinite;
pointer-events: none;
z-index: -1;
}
.emoji-card > * {
position: relative;
z-index: 1;
}
/*---------------------------*/
.emoji-card.gray { border-color: gray; }
@keyframes auraGlow {
0% { box-shadow: 0 0 10px 0 rgba(0,0,0,0.3); }
50% { box-shadow: 0 0 25px 8px currentColor; }
100% { box-shadow: 0 0 10px 0 rgba(0,0,0,0.3); }
}
/* Applique une animation d'aura par rareté */
.emoji-card.green {
color: darkgreen;
animation: auraGlow 3s infinite ease-in-out;
}
.emoji-card.purple {
color: purple;
animation: auraGlow 3s infinite ease-in-out;
}
.emoji-card.red {
color: darkred;
animation: auraGlow 3s infinite ease-in-out;
}
.emoji-card.gold {
color: goldenrod;
animation: auraGlow 3s infinite ease-in-out;
}
/* Couleurs des noms selon rareté */
.emoji-name.green { color: darkgreen; }
.emoji-name.purple { color: purple; }
.emoji-name.red { color: darkred; }
.emoji-name.gold { color: goldenrod; }
.emoji-name.gray { color: gray; }
.emoji-card .emoji {
font-size: 50px;
margin-bottom: 10px;
}
.emoji-name {
font-weight: bold;
font-size: 1.2rem;
margin-top: 5px;
color: purple;
}
.emoji-level {
font-size: 1rem;
font-weight: bold;
color: #3c2f2f;
margin-bottom: 10px;
}
.action-buttons {
display: flex;
justify-content: center;
gap: 60px;
margin-top: 20px;
}
.btn {
background: #f2e6c9;
border: 3px solid #000;
padding: 10px 25px;
font-size: 1.2rem;
font-weight: bold;
border-radius: 8px;
cursor: pointer;
transition: transform 0.2s ease;
box-shadow: 3px 3px 0 #000;
}
.btn:hover {
transform: scale(1.05);
background-color: #e5d6b8;
}
.detail-icon {
position: absolute;
top: 8px;
right: 10px;
cursor: pointer;
font-size: 18px;
color: #555;
}
.popup {
position: absolute;
top: 110%;
left: 50%;
transform: translateX(-50%);
background: #fff8e1;
color: #000;
padding: 10px;
border: 2px solid #000;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
display: none;
z-index: 10;
width: 180px;
font-size: 14px;
}
/* Filtre et Tri*/
.filter-bar {
margin-bottom: 30px;
color: #f8f5e0;
font-size: 1rem;
}
.filter-bar select {
margin: 0 10px;
padding: 5px 10px;
border-radius: 6px;
border: 2px solid #000;
background: #f2e6c9;
font-family: 'Georgia', serif;
}
/* Champs de Recherche */
.filter-bar input[type="text"] {
padding: 5px 10px;
border-radius: 6px;
border: 2px solid #000;
background: #f2e6c9;
font-family: 'Georgia', serif;
margin-right: 10px;
}
/* Style séléction créature pour combat / accouplement */
/* Etat séléctionné */
.emoji-card.selected {
outline: 4px solid #f8b435;
outline-offset: -4px;
box-shadow: 0 0 30px 10px rgba(248, 180, 53, 0.6);
}
#selection-status {
font-size: 1.1rem;
margin-bottom: 20px;
font-weight: bold;
color: #f9e8c0;
}
.selection-visual {
display: flex;
justify-content: center;
align-items: center;
gap: 15px;
margin-bottom: 30px;
}
.creature-tag {
padding: 8px 15px;
background: #f2e6c9;
border: 2px solid #000;
border-radius: 8px;
font-weight: bold;
font-size: 1.1rem;
box-shadow: 2px 2px 0 #000;
}
.vs-text {
font-size: 1.5rem;
font-weight: bold;
}

@ -0,0 +1,141 @@
document.addEventListener('DOMContentLoaded', () => {
// Copie tout ici
let selectedCards = [];
function toggleSelection(card) {
const id = card.dataset.id;
if (card.classList.contains('selected')) {
card.classList.remove('selected');
selectedCards = selectedCards.filter(c => c.dataset.id !== id);
} else {
if (selectedCards.length < 2) {
card.classList.add('selected');
selectedCards.push(card);
} else {
alert("Tu ne peux sélectionner que 2 créatures à la fois.");
}
}
updateSelectionDisplay();
}
function updateSelectionDisplay() {
const status = document.getElementById("selection-status");
const visual = document.getElementById("selection-visual");
if (selectedCards.length === 0) {
status.textContent = "Sélectionnez 2 créatures...";
visual.innerHTML = "";
} else if (selectedCards.length === 1) {
status.textContent = `1ère sélection : ${selectedCards[0].dataset.name}`;
visual.innerHTML = "";
} else {
status.textContent = "2 créatures sélectionnées !";
visual.innerHTML = `
<div class="creature-tag">${selectedCards[0].dataset.name}</div>
<span class="vs-text"> et </span>
<div class="creature-tag">${selectedCards[1].dataset.name}</div>
`;
}
}
function handleAction(type) {
if (selectedCards.length !== 2) {
alert("Tu dois sélectionner 2 créatures.");
return;
}
const name1 = selectedCards[0].dataset.name;
const name2 = selectedCards[1].dataset.name;
if (type === 'combat') {
console.log(`Combat : ${name1} contre ${name2}`);
} else if (type === 'reproduction') {
console.log(`Accouplement : ${name1} et ${name2}`);
}
// Réinitialiser après l'action
selectedCards.forEach(card => card.classList.remove('selected'));
selectedCards = [];
updateSelectionDisplay();
}
// Ouvre / Ferme la popup d'information
function togglePopup(id) {
const popup = document.getElementById('popup-' + id);
popup.style.display = (popup.style.display === 'block') ? 'none' : 'block';
}
// Fermer les autres popups en cliquant ailleurs
document.addEventListener('click', function(e) {
document.querySelectorAll('.popup').forEach(p => {
if (!p.contains(e.target) && !p.previousElementSibling.contains(e.target)) {
p.style.display = 'none';
}
});
});
// Fonction pour appliquer la recherche, les filtres et le tri
function applyFilters() {
const selectedColor = document.getElementById('rarete-filter').value;
const sortBy = document.getElementById('sort-select').value;
const searchTerm = document.getElementById('search-input').value.toLowerCase();
const cards = Array.from(document.querySelectorAll('.emoji-card'));
cards.forEach(card => {
const color = card.dataset.color;
const name = card.querySelector('.emoji-name')?.textContent.toLowerCase() ?? "";
const matchColor = !selectedColor || color === selectedColor;
const matchName = !searchTerm || name.includes(searchTerm);
if (matchColor && matchName) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
if (sortBy !== 'none') {
const container = document.querySelector('.emoji-container');
const visibleCards = cards.filter(c => c.style.display !== 'none');
visibleCards.sort((a, b) => {
const aVal = parseFloat(a.dataset[sortBy]);
const bVal = parseFloat(b.dataset[sortBy]);
return bVal - aVal;
});
visibleCards.forEach(card => container.appendChild(card));
}
}
// Appel Fonctionnalité de popup d'information
document.querySelectorAll('.detail-icon').forEach(icon => {
icon.addEventListener('click', (e) => {
const id = icon.parentElement.dataset.id;
togglePopup(id);
e.stopPropagation(); // empêche le clic daller à la carte
});
});
// Appel Fonctionnalité de sélection des cartes
document.querySelectorAll('.emoji-card').forEach(card => {
card.addEventListener('click', () => toggleSelection(card));
});
// Appel Fonctionnalité de combat et reproduction
document.querySelectorAll('.btn').forEach(button => {
button.addEventListener('click', () => {
const type = button.textContent.includes('Combattre') ? 'combat' : 'reproduction';
handleAction(type);
});
});
// Appel Fonctionnalité de recherche et filtres
document.getElementById('search-input').addEventListener('input', applyFilters);
document.getElementById('rarete-filter').addEventListener('change', applyFilters);
document.getElementById('sort-select').addEventListener('change', applyFilters);
});

@ -0,0 +1,62 @@
<?php
namespace App\Command;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class PopulateDBCommand extends Command
{
protected static $defaultName = 'app:populateDB';
private Connection $connection;
public function __construct(Connection $connection)
{
parent::__construct();
$this->connection = $connection;
}
protected function configure()
{
$this
->setDescription('Populate the database.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
// On supprime la table si elle existe déjà
$this->connection->executeStatement('DROP TABLE IF EXISTS rarity');
// On crée la table
$this->connection->executeStatement('
CREATE TABLE rarity (
id INT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
drop_rate FLOAT NOT NULL
)
');
// On peuple la table
$this->connection->executeStatement("INSERT INTO rarity (id, name, drop_rate) VALUES
(1, 'Common', 0.6),
(2, 'Rare', 0.2),
(3, 'Epic', 0.1),
(4, 'Mythical', 0.085),
(5, 'Legendary', 0.015)
");
$output->writeln('Base de données peuplée.');
} catch (\Exception $e) {
$output->writeln('<error>Erreur : ' . $e->getMessage() . '</error>');
return Command::FAILURE;
}
return Command::SUCCESS;
}
}

@ -2,15 +2,32 @@
namespace App\Controller;
use App\Entity\Emoji;
use App\Entity\Rarity;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Repository\RarityRepository;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Repository\EmojiRepository;
use Doctrine\ORM\EntityManagerInterface;
use App\Repository\EmojiRepository;
use Symfony\Component\HttpFoundation\JsonResponse;
#[Route('/emoji', name: 'emoji')]
class EmojiController extends AbstractController
{
private RarityRepository $rarityRepository;
private HttpClientInterface $httpClient;
public function __construct(RarityRepository $rarityRepository, HttpClientInterface $httpClient)
{
$this->rarityRepository = $rarityRepository;
$this->httpClient = $httpClient;
}
#[Route('/emoji', name: 'app_emoji')]
public function index(): Response
{
@ -20,6 +37,145 @@ class EmojiController extends AbstractController
]);
}
#[Route('/add/{code}', name: 'add')]
public function addEmojiDebug(string $code, EntityManagerInterface $entityManager) {
$emoji = new Emoji();
$emoji->setCode($code);
$emoji->setName('Default Name');
$emoji->setStrength(1.0);
$emoji->setToughness(1.0);
$emoji->setIntelligence(1.0);
$emoji->setSpeed(1.0);
$emoji->setFightsWon(0);
// On récupère une instance de Rarity existante (par exemple, la première)
$rarity = $this->getRarity();
if (!$rarity) {
throw new \RuntimeException('Aucun objet Rarity trouvé en base.');
}
$emoji->setRarity($rarity);
// Optionnel : définir parent1 et parent2 si tu veux tester avec des relations
// $emoji->setParent1(null);
// $emoji->setParent2(null);
$entityManager->persist($emoji);
$entityManager->flush();
return new Response();
}
private function getRarity(): Rarity {
$rarity = $this->rarityRepository->findAll();
$rand = mt_rand() / mt_getrandmax();
$sum = 0.0;
foreach($rarity as $r) {
$sum += $r->getDropRate();
if($sum > $rand) {
return $r;
}
}
return $rarity[0];
}
// Renvoi l'url de l'image issue de la fusion de deux emojis. Fonctionne avec un code et un emoji directement
private function getFusionUrl(string $emoji1, string $emoji2): string
{
$baseUrl = 'https://emojik.vercel.app/s/';
$size = 256;
// On encode les string pour l'url
$encodedEmoji1 = urlencode($emoji1);
$encodedEmoji2 = urlencode($emoji2);
return sprintf('%s%s_%s?size=%d', $baseUrl, $encodedEmoji1, $encodedEmoji2, $size);
}
#[Route('/fusion/{emoji1_id}/{emoji2_id}', name: 'fusion')]
public function reproduceEmoji(int $emoji1_id, int $emoji2_id, EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse {
$emoji1 = $emojiRepository->find($emoji1_id);
$emoji2 = $emojiRepository->find($emoji2_id);
if (!$emoji1 || !$emoji2) {
return new JsonResponse(['error' => 'One or both emojis not found'], 404);
}
$child = new Emoji();
$child->setName("Testenfant");
// Chance de fusion
$fusionRand = mt_rand() / mt_getrandmax();
if($fusionRand > 0.8) {
// Si les emoji parents sont des fusions on remonte l'arbre généalogique jusqu'à trouver un smiley normal
// On ne peut pas fusionner un smiley déjà fusionné
$temp1 = $emoji1;
while(str_contains($temp1->getCode(), "vercel")) {
$temp1 = $temp1->getParent1();
}
$temp2 = $emoji2;
while(str_contains($temp2->getCode(), "vercel")) {
$temp2 = $temp2->getParent2();
}
$child->setCode($this->getFusionUrl($temp1->getCode(), $temp2->getCode()));
} else {
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setCode($emoji1->getCode());
} else {
$child->setCode($emoji2->getCode());
}
}
// Lors d'une fusion un emoji récupère chaque stat d'un de ces deux parents de manière aléatoire
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setStrength($emoji1->getStrength());
} else {
$child->setStrength($emoji2->getStrength());
}
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setToughness($emoji1->getToughness());
} else {
$child->setToughness($emoji2->getToughness());
}
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setIntelligence($emoji1->getIntelligence());
} else {
$child->setIntelligence($emoji2->getIntelligence());
}
$rand = mt_rand() / mt_getrandmax();
if($rand > 0.5) {
$child->setSpeed($emoji1->getSpeed());
} else {
$child->setSpeed($emoji2->getSpeed());
}
$child->setFightsWon(0);
$child->setRarity($this->getRarity());
$child->setParent1($emoji1);
$child->setParent2($emoji2);
$entityManager->persist($child);
$entityManager->flush();
return new JsonResponse([
'message' => 'Child created',
'childId' => $child->getId()
]);
}
}
public function reproduceEmoji(Emoji $emoji1, Emoji $emoji2): Emoji {
return new Emoji();
}

@ -0,0 +1,66 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
#[Route('/', name: 'home')]
public function index(): Response
{
$emojis = [
[
'id' => 1,
'nom' => 'Bob',
'code' => '😊',
'force' => 12.5,
'robustesse' => 9.3,
'intelligence' => 7.8,
'vitesse' => 10.0,
'nbCombatGagne' => 3,
'rarete' => 2, // épique
],
[
'id' => 2,
'nom' => 'John',
'code' => '😭',
'force' => 5.1,
'robustesse' => 4.2,
'intelligence' => 3.3,
'vitesse' => 6.0,
'nbCombatGagne' => 1,
'rarete' => 1, // commun
],
[
'id' => 3,
'nom' => 'Rodolph',
'code' => '😁',
'force' => 20.0,
'robustesse' => 15.0,
'intelligence' => 18.0,
'vitesse' => 17.0,
'nbCombatGagne' => 10,
'rarete' => 4, // légendaire
]
];
// Ajout de la couleur selon la rareté
foreach ($emojis as &$emoji) {
$emoji['color'] = match ($emoji['rarete']) {
1 => 'green', // commun
2 => 'purple', // épique
3 => 'red', // mythique
4 => 'gold', // légendaire
default => 'gray'
};
}
return $this->render('home/index.html.twig', [
'emojis' => $emojis,
]);
}
}

@ -14,42 +14,73 @@ class Emoji
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $nom = null;
private ?string $name = null;
#[ORM\Column(length: 255)]
private ?string $code = null;
#[ORM\Column]
private ?float $force = null;
private ?float $strength = null;
#[ORM\Column]
private ?float $robustesse = null;
private ?float $toughness = null;
#[ORM\Column]
private ?float $intelligence = null;
#[ORM\Column]
private ?float $vitesse = null;
private ?float $speed = null;
#[ORM\Column]
private ?int $nbCombatGagne = null;
private ?int $fightsWon = null;
#[ORM\Column]
private ?int $rarete = null;
#[ORM\ManyToOne(targetEntity: Rarity::class)]
#[ORM\JoinColumn(nullable: false)]
private ?Rarity $rarity = null;
#[ORM\ManyToOne(targetEntity: self::class)]
#[ORM\JoinColumn(nullable: true)]
private ?Emoji $parent1 = null;
#[ORM\ManyToOne(targetEntity: self::class)]
#[ORM\JoinColumn(nullable: true)]
private ?Emoji $parent2 = null;
public function getParent1(): ?self
{
return $this->parent1;
}
public function setParent1(?self $parent1): self
{
$this->parent1 = $parent1;
return $this;
}
public function getParent2(): ?self
{
return $this->parent2;
}
public function setParent2(?self $parent2): self
{
$this->parent2 = $parent2;
return $this;
}
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
public function getName(): ?string
{
return $this->nom;
return $this->name;
}
public function setNom(string $nom): self
public function setName(string $name): self
{
$this->nom = $nom;
$this->name = $name;
return $this;
}
@ -64,25 +95,25 @@ class Emoji
return $this;
}
public function getForce(): ?float
public function getStrength(): ?float
{
return $this->force;
return $this->strength;
}
public function setForce(float $force): self
public function setStrength(float $strength): self
{
$this->force = $force;
$this->strength = $strength;
return $this;
}
public function getRobustesse(): ?float
public function getToughness(): ?float
{
return $this->robustesse;
return $this->toughness;
}
public function setRobustesse(float $robustesse): self
public function setToughness(float $toughness): self
{
$this->robustesse = $robustesse;
$this->toughness = $toughness;
return $this;
}
@ -97,36 +128,36 @@ class Emoji
return $this;
}
public function getVitesse(): ?float
public function getSpeed(): ?float
{
return $this->vitesse;
return $this->speed;
}
public function setVitesse(float $vitesse): self
public function setSpeed(float $speed): self
{
$this->vitesse = $vitesse;
$this->speed = $speed;
return $this;
}
public function getNbCombatGagne(): ?int
public function getFightsWon(): ?int
{
return $this->nbCombatGagne;
return $this->fightsWon;
}
public function setNbCombatGagne(int $nbCombatGagne): self
public function setFightsWon(int $fightsWon): self
{
$this->nbCombatGagne = $nbCombatGagne;
$this->fightsWon = $fightsWon;
return $this;
}
public function getRarete(): ?int
public function getRarity(): ?Rarity
{
return $this->rarete;
return $this->rarity;
}
public function setRarete(int $rarete): self
public function setRarity(Rarity $rarity): self
{
$this->rarete = $rarete;
$this->rarity = $rarity;
return $this;
}

@ -0,0 +1,48 @@
<?php
namespace App\Entity;
use App\Repository\RarityRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: RarityRepository::class)]
class Rarity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 50, unique: true)]
private string $name;
#[ORM\Column(type: 'float')]
private float $dropRate;
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getDropRate(): float
{
return $this->dropRate;
}
public function setDropRate(float $dropRate): self
{
$this->dropRate = $dropRate;
return $this;
}
}

@ -0,0 +1,48 @@
<?php
namespace App\Repository;
use App\Entity\Rarity;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Rarity>
*
* @method Rarity|null find($id, $lockMode = null, $lockVersion = null)
* @method Rarity|null findOneBy(array $criteria, array $orderBy = null)
* @method Rarity[] findAll()
* @method Rarity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class RarityRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Rarity::class);
}
// /**
// * @return Rarity[] Returns an array of Rarity objects
// */
// public function findByExampleField($value): array
// {
// return $this->createQueryBuilder('r')
// ->andWhere('r.exampleField = :val')
// ->setParameter('val', $value)
// ->orderBy('r.id', 'ASC')
// ->setMaxResults(10)
// ->getQuery()
// ->getResult()
// ;
// }
// public function findOneBySomeField($value): ?Rarity
// {
// return $this->createQueryBuilder('r')
// ->andWhere('r.exampleField = :val')
// ->setParameter('val', $value)
// ->getQuery()
// ->getOneOrNullResult()
// ;
// }
}

@ -126,18 +126,6 @@
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/messenger": {
"version": "6.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "6.0",
"ref": "ba1ac4e919baba5644d31b57a3284d6ba12d52ee"
},
"files": [
"./config/packages/messenger.yaml"
]
},
"symfony/monolog-bundle": {
"version": "3.10",
"recipe": {

@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
{% block stylesheets %}

@ -0,0 +1,69 @@
{% extends 'base.html.twig' %}
{% block title %}Accueil - Ma collection de créatures{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('css/home.css') }}">
{% endblock %}
{% block body %}
<h1>🧬 Ma collection de créatures 🐾</h1>
<div class="filter-bar">
<label for="search-input">🔍 Rechercher un nom :</label>
<input type="text" id="search-input" placeholder="ex: Bob..." />
<label for="rarete-filter">Filtrer par rareté :</label>
<select id="rarete-filter">
<option value="">Toutes</option>
<option value="green">Communes</option>
<option value="purple">Épiques</option>
<option value="red">Mythiques</option>
<option value="gold">Légendaires</option>
</select>
<label for="sort-select">Trier par :</label>
<select id="sort-select">
<option value="none">--</option>
<option value="level">Level</option>
<option value="force">Force</option>
<option value="vitesse">Vitesse</option>
</select>
</div>
<div id="selection-status">Sélectionnez 2 créatures...</div>
<div class="emoji-container">
{% for emoji in emojis %}
<div class="emoji-card {{ emoji.color }}" data-id="{{ emoji.id }}" data-name="{{ emoji.nom }}"
data-color="{{ emoji.color }}" data-level="{{ emoji.nbCombatGagne }}"
data-force="{{ emoji.force }}" data-vitesse="{{ emoji.vitesse }}">
<div class="emoji-level">Level {{ emoji.nbCombatGagne }}</div>
<div class="emoji">{{ emoji.code }}</div>
<div class="emoji-name {{ emoji.color }}">{{ emoji.nom }}</div>
<div class="detail-icon"></div>
<div class="popup" id="popup-{{ emoji.id }}">
<strong>Stats :</strong><br>
Force: {{ emoji.force }}<br>
Robustesse: {{ emoji.robustesse }}<br>
Intelligence: {{ emoji.intelligence }}<br>
Vitesse: {{ emoji.vitesse }}
</div>
</div>
{% endfor %}
</div>
<div id="selection-visual" class="selection-visual"></div>
<div class="action-buttons">
<button class="btn" ">⚔️ Combattre</button>
<button class="btn" ">💞 Reproduire</button>
</div>
{% endblock %}
{% block javascripts %}
<script src="{{ asset('js/home.js') }}"></script>
{% endblock %}
Loading…
Cancel
Save