Compare commits

..

31 Commits

Author SHA1 Message Date
Cleo EIRAS a3cecbc9d6 👻
15 hours ago
samuel a02b25f61b Admin panel fixed
23 hours ago
samuel 6d563bd831 Admin : routes access control for /users + possibility to extend elsewhere
24 hours ago
Mathis MOULIN 7f602aec33 Ajout de la liason home avec combat !
1 day ago
Mathis MOULIN 2a5d0ccb5a Faux push
1 day ago
Mathis MOULIN 51b56c44b3 Tout fonction reproduction fonction en back et front
1 day ago
Mathis MOULIN b7e08bb775 Oublie fichier
1 day ago
Mathis MOULIN d0bdb9c067 Ajout Front Jade v1 WIP
1 day ago
Mathis MOULIN a66a4a3d95 Merge Samuel
1 day ago
Mathis MOULIN 0c287484d2 Hierachie familliale emoji
1 day ago
samuel 0c51e7fbd7 Add user_emojis -> emojis of a user fix + refactor + Add Service
2 days ago
Jade VAN BRABANDT 33237d6ca9 idk i'll see later
2 days ago
Jade VAN BRABANDT 0890abc78c Reproduction, La DA es ter
2 days ago
Jade VAN BRABANDT 3558e4c289 IHateSymfony
2 days ago
Mathis MOULIN 88da18817d rebase on last version mathis
2 days ago
Jade VAN BRABANDT f0bc442188 Merge remote-tracking branch 'origin/mathis' into Jade-Front
2 days ago
Mathis MOULIN feef24045c Modif fight for anim
2 days ago
Mathis MOULIN 535531440f Merge taff Raff & Samuel
2 days ago
Jade VAN BRABANDT c300cfee26 Reproduction with id
2 days ago
Jade VAN BRABANDT 8d4b76c309 Merge remote-tracking branch 'origin/cleo' into Jade-Front
3 days ago
Jade VAN BRABANDT 0af007b902 Change url
3 days ago
Cleo EIRAS f24545f3e0 Merge branch 'cleo' of https://codefirst.iut.uca.fr/git/cleo.eiras/EvoLyte into cleo
3 days ago
Cleo EIRAS 22e29336d8 Retourne mieux reproduction
3 days ago
samuel 43e653bb06 API Plateforme entity ressources
3 days ago
samuel e57722cabb Auth with Symfony Form
3 days ago
Jade VAN BRABANDT ced39abea8 Reproduction, La DA es ter
1 week ago
Jade VAN BRABANDT 0bbd984d79 IHateSymfony
1 week ago
Jade VAN BRABANDT 3b314f30e5 Merge remote-tracking branch 'origin/cleo' into Jade-Front
1 week ago
Jade VAN BRABANDT ca6702ea60 Merge cleo
1 week ago
BelsethUwU 44309c2b9b Implements the use of custom emoji
2 weeks ago
BelsethUwU 36893c8bc8 Combat page with animation
2 weeks ago

@ -0,0 +1,13 @@
# How to run
`composer update`
`php bin/console make:migration`
`php bin/console doctrine:migrations:migrate`
`php bin/console app:populateDB`
`php bin/console app:popDBEmojiAvai`
`symfony server:start`

4
composer.lock generated

@ -9486,7 +9486,7 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": {},
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
@ -9494,6 +9494,6 @@
"ext-ctype": "*", "ext-ctype": "*",
"ext-iconv": "*" "ext-iconv": "*"
}, },
"platform-dev": [], "platform-dev": {},
"plugin-api-version": "2.6.0" "plugin-api-version": "2.6.0"
} }

@ -11,6 +11,6 @@ return [
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true],
]; ];

@ -1,24 +1,24 @@
# see https://symfony.com/doc/current/reference/configuration/framework.html # see https://symfony.com/doc/current/reference/configuration/framework.html
framework: framework:
secret: '%env(APP_SECRET)%' secret: "%env(APP_SECRET)%"
#csrf_protection: true #csrf_protection: true
http_method_override: false http_method_override: false
# Enables session support. Note that the session will ONLY be started if you read or write from it. # Enables session support. Note that the session will ONLY be started if you read or write from it.
# Remove or comment this section to explicitly disable session support. # Remove or comment this section to explicitly disable session support.
session: session:
handler_id: null handler_id: null
cookie_secure: auto cookie_secure: auto
cookie_samesite: lax cookie_samesite: lax
storage_factory_id: session.storage.factory.native storage_factory_id: session.storage.factory.native
#esi: true #esi: true
#fragments: true #fragments: true
php_errors: php_errors:
log: true log: true
when@test: when@test:
framework: framework:
test: true test: true
session: session:
storage_factory_id: session.storage.factory.mock_file storage_factory_id: session.storage.factory.mock_file

@ -1,12 +1,12 @@
framework: framework:
router: router:
utf8: true utf8: true
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands. # Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
#default_uri: http://localhost #default_uri: http://localhost
when@prod: when@prod:
framework: framework:
router: router:
strict_requirements: null strict_requirements: null

@ -40,14 +40,14 @@ security:
access_control: access_control:
- { path: ^/login, roles: PUBLIC_ACCESS } - { path: ^/login, roles: PUBLIC_ACCESS }
- { path: ^/register, roles: PUBLIC_ACCESS } - { path: ^/register, roles: PUBLIC_ACCESS }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/, roles: ROLE_USER } - { path: ^/, roles: ROLE_USER }
when@test: when@test:
security: security:
firewalls:
main:
security: false
password_hashers: password_hashers:
# By default, password hashers are resource intensive and take time. This is # By default, password hashers are resource intensive and take time. This is
# important to generate secure password hashes. In tests however, secure hashes # important to generate secure password hashes. In tests however, secure hashes

@ -0,0 +1,90 @@
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f4f6f8;
color: #333;
margin: 2rem auto;
max-width: 800px;
padding: 1rem;
}
h1 {
color: #2c3e50;
font-size: 2rem;
margin-bottom: 1rem;
text-align: center;
}
h2 {
color: #34495e;
font-size: 1.4rem;
margin-top: 2rem;
margin-bottom: 1rem;
border-bottom: 2px solid #ccc;
padding-bottom: 0.2rem;
}
section {
background: white;
border-radius: 8px;
padding: 1.5rem;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
margin-bottom: 1.5rem;
}
ul#user-list {
list-style: none;
padding: 0;
}
#user-list li {
background: #ecf0f1;
margin-bottom: 0.5rem;
padding: 0.8rem 1rem;
border-radius: 6px;
display: flex;
justify-content: space-between;
align-items: center;
}
#user-list li button {
background-color: #e74c3c;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 0.9rem;
}
#user-list li button:hover {
background-color: #c0392b;
}
form#add-user-form {
display: flex;
flex-direction: column;
gap: 0.8rem;
}
form#add-user-form input,
form#add-user-form select {
padding: 0.6rem;
border: 1px solid #bdc3c7;
border-radius: 4px;
font-size: 1rem;
}
form#add-user-form button {
background-color: #3498db;
color: white;
border: none;
padding: 0.7rem;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
}
form#add-user-form button:hover {
background-color: #2980b9;
}

@ -4,6 +4,13 @@ body {
text-align: center; text-align: center;
} }
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow-x: hidden;
}
h1 { h1 {
font-size: 3rem; font-size: 3rem;
margin-bottom: 30px; margin-bottom: 30px;
@ -151,6 +158,12 @@ h1 {
margin-bottom: 10px; margin-bottom: 10px;
} }
.emoji img {
vertical-align: middle;
width: 68px;
height: 68px;
}
.emoji-name { .emoji-name {
font-weight: bold; font-weight: bold;
font-size: 1.2rem; font-size: 1.2rem;
@ -189,10 +202,16 @@ h1 {
background-color: #e5d6b8; background-color: #e5d6b8;
} }
.detail-icon {
.btn-logout {
width: 238px;
height: 50px;
}
.detail-icon-hierarchy {
position: absolute; position: absolute;
top: 8px; top: 8px;
right: 10px; left: 10px;
cursor: pointer; cursor: pointer;
font-size: 18px; font-size: 18px;
color: #555; color: #555;
@ -215,6 +234,112 @@ color: #555;
font-size: 14px; font-size: 14px;
} }
/* Partie Hierachie*/
.hierarchy-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
display: none; /* sera activé par JS */
z-index: 1000;
}
.hierarchy-overlay {
position: fixed; /* ← Important ! */
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0,0,0,0.6);
z-index: 1001;
}
.hierarchy-content {
position: fixed; /* ← Pour rester centré par rapport au viewport */
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border-radius: 12px;
z-index: 1002;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
max-width: 90%;
width: 500px;
}
.close-btn {
position: absolute;
top: 10px;
right: 20px;
font-size: 30px;
cursor: pointer;
}
.hierarchy-graph {
display: grid;
grid-template-columns: 1fr auto 1fr;
grid-template-rows: auto auto;
grid-template-areas:
"parent1 arrows parent2"
". child .";
gap: 10px 30px;
align-items: center;
justify-items: center;
margin-top: 30px;
}
.child-block {
grid-area: child;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.parent-block {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.parent-block .emoji {
font-size: 40px;
margin-bottom: 5px;
}
.parent-block .name {
font-size: 14px;
color: #444;
font-style: italic;
}
.arrows {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.arrow {
font-size: 24px;
margin: 10px 0;
}
.child {
grid-area: child;
font-size: 50px;
padding: 10px;
}
.connector {
grid-column: 2;
font-size: 30px;
color: #444;
}
/* Filtre et Tri*/ /* Filtre et Tri*/
.filter-bar { .filter-bar {
margin-bottom: 30px; margin-bottom: 30px;

@ -0,0 +1,118 @@
body {
background-color: #314e57;
font-family: 'Georgia', serif;
text-align: center;
}
.login-container {
background-color: #3b6068;
border: 4px solid #000;
border-radius: 15px;
padding: 40px 30px;
margin: 60px auto;
width: 90%;
max-width: 400px;
box-shadow: 0 0 30px rgba(0,0,0,0.6);
color: #f8f5e0;
font-family: 'Georgia', serif;
}
.login-title {
font-size: 2.5rem;
color: #f8b435;
margin-bottom: 20px;
text-shadow: 2px 2px 4px #000;
}
.login-error {
background-color: #8b0000;
color: #fff8e1;
padding: 10px;
border: 2px solid #000;
border-radius: 8px;
margin-bottom: 20px;
font-weight: bold;
}
.already-logged {
background-color: #f2e6c9;
color: #000;
padding: 12px;
border: 2px dashed #000;
border-radius: 8px;
margin-bottom: 20px;
font-size: 0.95rem;
}
.logout-link {
display: inline-block;
margin-top: 10px;
color: #8b0000;
font-weight: bold;
text-decoration: underline;
}
.login-form {
display: flex;
flex-direction: column;
gap: 15px;
}
.login-form label {
text-align: left;
color: #f8f5e0;
font-weight: bold;
}
.login-form input[type="text"],
.login-form input[type="password"] {
padding: 10px;
border: 2px solid #000;
border-radius: 6px;
background-color: #f2e6c9;
font-family: 'Georgia', serif;
font-size: 1rem;
box-shadow: inset 2px 2px 5px rgba(0,0,0,0.2);
}
.btn-login {
background-color: #f8b435;
color: #000;
font-weight: bold;
border: 3px solid #000;
padding: 10px;
border-radius: 8px;
font-size: 1.2rem;
cursor: pointer;
box-shadow: 3px 3px 0 #000;
transition: transform 0.2s ease;
}
.btn-login:hover {
transform: scale(1.05);
background-color: #e09f30;
}
.no-account {
margin-top: 30px;
color: #f8f5e0;
}
.btn-register {
display: inline-block;
margin-top: 10px;
background-color: #f2e6c9;
color: #000;
border: 2px solid #000;
padding: 8px 20px;
border-radius: 6px;
text-decoration: none;
font-weight: bold;
box-shadow: 2px 2px 0 #000;
transition: transform 0.2s ease;
}
.btn-register:hover {
transform: scale(1.05);
background-color: #e5d6b8;
}

@ -0,0 +1,88 @@
body {
background-color: #314e57 !important;
font-family: 'Georgia', serif;
text-align: center;
}
.register-container {
background-color: #3b6068;
border: 4px solid #000;
border-radius: 15px;
padding: 40px 30px;
margin: 60px auto;
width: 90%;
max-width: 400px;
box-shadow: 0 0 30px rgba(0,0,0,0.6);
color: #f8f5e0;
font-family: 'Georgia', serif;
}
.register-title {
font-size: 2.5rem;
color: #f8b435;
margin-bottom: 20px;
text-shadow: 2px 2px 4px #000;
}
.register-form label {
text-align: left;
color: #f8f5e0;
font-weight: bold;
}
.register-form input[type="text"],
.register-form input[type="password"] {
padding: 10px;
border: 2px solid #000;
border-radius: 6px;
background-color: #f2e6c9;
font-family: 'Georgia', serif;
font-size: 1rem;
box-shadow: inset 2px 2px 5px rgba(0,0,0,0.2);
width: 100%;
margin-bottom: 15px;
}
.btn-register-submit {
background-color: #f8b435;
color: #000;
font-weight: bold;
border: 3px solid #000;
padding: 10px;
border-radius: 8px;
font-size: 1.2rem;
cursor: pointer;
box-shadow: 3px 3px 0 #000;
transition: transform 0.2s ease;
width: 100%;
margin-bottom: 20px;
}
.btn-register-submit:hover {
transform: scale(1.05);
background-color: #e09f30;
}
.already-account {
color: #f8f5e0;
font-size: 0.95rem;
}
.btn-login-link {
display: inline-block;
margin-top: 10px;
background-color: #f2e6c9;
color: #000;
border: 2px solid #000;
padding: 8px 20px;
border-radius: 6px;
text-decoration: none;
font-weight: bold;
box-shadow: 2px 2px 0 #000;
transition: transform 0.2s ease;
}
.btn-login-link:hover {
transform: scale(1.05);
background-color: #e5d6b8;
}

@ -1,5 +1,7 @@
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Copie tout ici
let selectedCards = []; let selectedCards = [];
function toggleSelection(card) { function toggleSelection(card) {
@ -40,44 +42,33 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
function handleAction(type) { async function handleAction(type) {
if (selectedCards.length !== 2) { if (selectedCards.length !== 2) {
alert("Tu dois sélectionner 2 créatures."); alert("Tu dois sélectionner 2 créatures.");
return; return;
} }
const name1 = selectedCards[0].dataset.name; const id1 = selectedCards[0].dataset.id;
const name2 = selectedCards[1].dataset.name; const id2 = selectedCards[1].dataset.id;
const idUser = document.getElementById('user-data').dataset.userId;
if (type === 'reproduction') {
window.location.href = `https://localhost:8000/reproduction/${encodeURIComponent(idUser)}/${encodeURIComponent(id1)}/${encodeURIComponent(id2)}`;
return; // on quitte ici
}
if (type === 'combat') { if (type === 'combat') {
const id1 = selectedCards[0].dataset.id;
const id2 = selectedCards[1].dataset.id;
const t1 = 4;
const t2 = 6;
console.log(`Combat : ${name1} contre ${name2}`);
//window.location.href = `/emoji/fight/${t2}/${t1}`;
//window.location.href = `/emoji/fight/${id1}/${id2}`;
fetch(`/emoji/fight/${t2}/${t1}`, { method: 'GET',
headers: {
'Accept': 'application/json'
}}).then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Données reçues :', data);
})
.catch(error => {
console.error('Erreur lors de la requête :', error);
});
} else if (type === 'reproduction') { window.location.href = `https://localhost:8000/combat/${encodeURIComponent(id1)}/${encodeURIComponent(id2)}`;
console.log(`Accouplement : ${name1} et ${name2}`);
return; // on quitte ici
} }
// Réinitialiser après l'action (seulement si pas redirigé) // Réinitialiser si pas redirection
selectedCards.forEach(card => card.classList.remove('selected')); selectedCards.forEach(card => card.classList.remove('selected'));
selectedCards = []; selectedCards = [];
updateSelectionDisplay(); updateSelectionDisplay();
@ -91,6 +82,63 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
// Ouvre / Ferme la popup de hierachie de famille
async function toggleHierachiPopup(id) {
try {
const response = await fetch(`/emojis/${id}/getParents/`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
if (!response.ok) throw new Error("Erreur serveur : " + response.status);
const data = await response.json();
// Injection dynamique dans la popup
const parent1El = document.getElementById(`parent1-${id}`);
if (data.codeParent1 && data.codeParent1.includes('vercel')) {
parent1El.innerHTML = `<img src="${data.codeParent1}" alt="Parent 1" width="48" height="48">`;
} else {
parent1El.innerText = data.codeParent1 || '❓';
}
const parent2El = document.getElementById(`parent2-${id}`);
if (data.codeParent2 && data.codeParent2.includes('vercel')) {
parent2El.innerHTML = `<img src="${data.codeParent2}" alt="Parent 2" width="48" height="48">`;
} else {
parent2El.innerText = data.codeParent2 || '❓';
}
const childEl = document.getElementById(`child-${id}`);
if (data.codeEnfant && data.codeEnfant.includes('vercel')) {
childEl.innerHTML = `<img src="${data.codeEnfant}" alt="Emoji fusionné" width="64" height="64">`;
} else {
childEl.innerText = data.codeEnfant || '❓';
}
document.getElementById(`name-parent1-${id}`).innerText = data.nameParent1 || 'inconnu';
document.getElementById(`name-parent2-${id}`).innerText = data.nameParent2 || 'inconnu';
document.getElementById(`name-child-${id}`).innerText = data.nameEnfant || 'inconnu';
console.log("Parents récupérés :", data);
// Affichage plein écran
const popup = document.getElementById(`popup-hiera-${id}`);
if (popup) popup.style.display = 'block';
} catch (error) {
console.error("Erreur lors de la récupération des parents :", error);
alert("Une erreur est survenue lors de l'affichage de la hiérarchie.");
}
}
function closeHierarchyPopup(id) {
const popup = document.getElementById(`popup-hiera-${id}`);
if (popup) popup.style.display = 'none';
}
// Fermer les autres popups en cliquant ailleurs // Fermer les autres popups en cliquant ailleurs
document.addEventListener('click', function(e) { document.addEventListener('click', function(e) {
document.querySelectorAll('.popup').forEach(p => { document.querySelectorAll('.popup').forEach(p => {
@ -98,6 +146,16 @@ document.addEventListener('DOMContentLoaded', () => {
p.style.display = 'none'; p.style.display = 'none';
} }
}); });
// Partie pour fermer la popup de hiérarchie
document.querySelectorAll('.close-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const id = btn.dataset.id; // On récupère l'id depuis l'attribut data-id
const popup = document.getElementById(`popup-hiera-${id}`);
if (popup) popup.style.display = 'none';
e.stopPropagation(); // Empêche la propagation du clic
});
});
}); });
// Fonction pour appliquer la recherche, les filtres et le tri // Fonction pour appliquer la recherche, les filtres et le tri
@ -123,7 +181,7 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
if (sortBy !== 'none') { if (sortBy !== 'none') {
const container = document.getElementById('collection-container'); // ✅ corrigé const container = document.getElementById('collection-container');
const visibleCards = cards.filter(c => c.style.display !== 'none'); const visibleCards = cards.filter(c => c.style.display !== 'none');
visibleCards.sort((a, b) => { visibleCards.sort((a, b) => {
@ -149,6 +207,18 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
}); });
// Appel Fonctionnalité de popup de hierachie de famille
document.querySelectorAll('.detail-icon-hierarchy').forEach(icon => {
icon.addEventListener('click', (e) => {
const card = icon.closest('.emoji-card');
const id = card?.dataset.id;
if (id) {
toggleHierachiPopup(id);
}
e.stopPropagation(); // évite que ça sélectionne la carte
});
});
// Appliquer l'écouteur à toutes les cartes (base + collection) // Appliquer l'écouteur à toutes les cartes (base + collection)
document.querySelectorAll('.emoji-container').forEach(container => { document.querySelectorAll('.emoji-container').forEach(container => {
container.addEventListener('click', (e) => { container.addEventListener('click', (e) => {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

@ -0,0 +1,65 @@
document.addEventListener('DOMContentLoaded', () => {
const left = document.querySelector('.left-emoji');
const right = document.querySelector('.right-emoji');
const winnerText = document.getElementById('winner-text');
fetch(`https://localhost:8000/emojis/fight/${id1}/${id2}`)
.then(response => response.json())
.then(data => {
const { leftEmoji, rightEmoji, winner } = data;
left.dataset.content = leftEmoji;
right.dataset.content = rightEmoji;
window.winnerSide = winner === 'left' ? 'left' : 'right';
window.loserSide = winner === 'left' ? 'right' : 'left';
insertEmojiOrImage(left);
insertEmojiOrImage(right);
left.classList.add('slide-in-left');
right.classList.add('slide-in-right');
setTimeout(() => {
left.style.left = '100px';
right.style.left = '300px';
left.classList.add('fight-animation');
right.classList.add('fight-animation');
setTimeout(() => {
left.classList.remove('fight-animation');
right.classList.remove('fight-animation');
const loser = window.loserSide === 'left' ? left : right;
loser.classList.add('loser-fly-up');
const winnerEl = window.winnerSide === 'left' ? left : right;
winnerEl.classList.add('winner-center', 'winner-crown');
let name = winnerEl.dataset.content;
if (/^https?:\/\//.test(name)) {
winnerText.innerHTML = `<img src="${name}" style="width:64px">a gagné ! 🎉`;
} else {
winnerText.textContent = `${name} a gagné ! 🎉`;
}
winnerText.style.display = 'block';
}, 1500);
}, 3000);
});
function insertEmojiOrImage(el) {
const value = el.dataset.content;
if (/^https?:\/\//.test(value)) {
const img = document.createElement('img');
img.src = value;
img.className = 'emoji-img';
el.appendChild(img);
} else {
el.textContent = value;
}
}
document.getElementById('btn-retour').addEventListener('click', () => {
window.location.href = `https://localhost:8000/`;
});
});

@ -0,0 +1,67 @@
document.addEventListener('DOMContentLoaded', () => {
const left = document.querySelector('.left-emoji');
const right = document.querySelector('.right-emoji');
const childText = document.getElementById('child-text');
const heartGif = document.getElementById('heart-gif');
fetch(`https://localhost:8000/emojis/fusion/${id_user}/${id_left}/${id_right}`)
.then(response => response.json())
.then(data => {
child_emoji=data['baby'];
left.dataset.content=data['mommy'];
right.dataset.content=data['daddy'];
function insertEmojiOrImage(el) {
const value = el.dataset.content;
console.log(value)
if (/^https?:\/\//.test(value)) {
const img = document.createElement('img');
img.src = value;
img.className = 'emoji-img';
el.appendChild(img);
} else {
el.textContent = value;
}
}
insertEmojiOrImage(left);
insertEmojiOrImage(right);
left.classList.add('slide-in-left');
right.classList.add('slide-in-right');
setTimeout(() => {
if (/^https?:\/\//.test(child_emoji)) {
emojiHtml = `
<div class="emoji-glow-img">
<img src="${child_emoji}" style="width:64px; height:64px">
</div>`;
} else {
emojiHtml = `
<span class="emoji-glow-text" data-emoji="${child_emoji}">${child_emoji}</span>`;
}
childText.innerHTML = `
<div class="emoji-wrapper">
${emojiHtml}
</div>`;
childText.classList.add('growing-animation');
childText.style.display = 'block';
heartGif.style.display = 'block';
heartGif.classList.add('fade-in');
}, 3000);
})
.catch(error => {
console.error('Erreur:', error);
});
document.getElementById('btn-retour').addEventListener('click', () => {
window.location.href = `https://localhost:8000/`;
});
});

@ -0,0 +1,157 @@
body {
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-image: url("../media/boxing_ring.jpg");
background-position: center;
background-repeat: no-repeat;
background-size: cover;
overflow: hidden;
font-family: Arial, sans-serif;
}
.arena {
position: relative;
width: 400px;
height: 150px;
}
.emoji {
position: absolute;
top: 50%;
font-size: 5rem;
transform: translate(-50%, -50%);
}
.emoji-img {
width: 5rem;
height: 5rem;
object-fit: contain;
}
.winner-center .emoji-img {
width: 6rem;
height: 6rem;
}
.loser-fly-up .emoji-img {
opacity: 0.6;
}
.left-emoji {
left: -100px;
}
.right-emoji {
left: 500px;
}
@keyframes slideInLeft {
to {
left: 100px;
}
}
@keyframes slideInRight {
to {
left: 300px;
}
}
.slide-in-left {
animation: slideInLeft 3s forwards;
}
.slide-in-right {
animation: slideInRight 3s forwards;
}
@keyframes fightShake {
0%,
100% {
transform: translate(-50%, -50%) translateX(0);
}
20%,
60% {
transform: translate(-50%, -50%) translateX(-15px);
}
40%,
80% {
transform: translate(-50%, -50%) translateX(15px);
}
}
.fight-animation {
animation: fightShake 0.5s 3;
}
@keyframes flyUpFade {
to {
opacity: 0;
transform: translate(-50%, calc(-150px - 50%));
}
}
.loser-fly-up {
animation: flyUpFade 3s forwards;
position: absolute;
}
@keyframes moveToCenter {
to {
left: 50%;
transform: translate(-50%, -50%);
}
}
.winner-center {
animation: moveToCenter 1s forwards;
position: absolute !important;
top: 50% !important;
font-size: 6rem;
z-index: 10;
}
.winner-crown::after {
content: "👑";
position: absolute;
top: -1.5rem;
left: 50%;
transform: translateX(-50%);
font-size: 2rem;
}
#winner-text {
margin-top: 1rem;
font-size: 2rem;
text-align: center;
display: none;
}
/* Bouton de retour */
.btn-retour {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
padding: 10px 25px;
font-size: 1.1rem;
font-weight: bold;
background-color: #f2e6c9;
border: 2px solid #000;
border-radius: 8px;
cursor: pointer;
box-shadow: 2px 2px 0 #000;
transition: transform 0.2s ease;
z-index: 9999; /* pour quil soit toujours au-dessus */
}
.btn-retour:hover {
transform: translateX(-50%) scale(1.05);
background-color: #e0d1b3;
}

@ -0,0 +1,179 @@
body {
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-image: url("../media/champ_amour.webp");
background-position: center;
background-repeat: no-repeat;
background-size: cover;
overflow: hidden;
font-family: Arial, sans-serif;
}
.field {
position: relative;
width: 400px;
height: 150px;
}
.emoji {
position: absolute;
top: 50%;
font-size: 5rem;
transform: translate(-50%, -50%);
}
.emoji-img {
width: 5rem;
height: 5rem;
object-fit: contain;
}
.left-emoji {
left: -100px;
}
.right-emoji {
left: 500px;
}
@keyframes slideInLeft {
to {
left: 200px;
opacity:0;
}
}
@keyframes slideInRight {
to {
left: 200px;
opacity:0;
}
}
.slide-in-left {
animation: slideInLeft 3s forwards;
}
.slide-in-right {
animation: slideInRight 3s forwards;
}
@keyframes Fade {
to {
opacity: 0;
}
}
.fading-animation {
animation: Fade 3s forwards;
}
@keyframes growing {
to {
transform: scale(5);
}
}
.growing-animation {
animation: growing 5s forwards;
}
#child-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
flex-direction: column;
}
.emoji-wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
.emoji-glow-text {
position: relative;
display: inline-block;
font-size: 5rem;
}
.emoji-glow-text::before {
content: attr(data-emoji);
position: absolute;
top: 0;
left: 0;
filter: blur(1px) brightness(10);
opacity: 0.9;
z-index: -1;
}
.emoji-glow-img {
position: relative;
display: inline-block;
}
.emoji-glow-img::before {
content: "";
position: absolute;
top: 0;
left: 0;
filter: blur(1px) brightness(10);
opacity: 0.9;
z-index: -1;
}
.birth-text{
font-size: 2rem;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
margin-top: 20px;
}
#heart-gif{
position: absolute;
display:none;
background-image: url("../media/hearts.gif");
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30vw;
height: 50vh;
background-repeat: no-repeat;
background-size: 100% 100%;
z-index: -1;
}
/* Bouton de retour */
.btn-retour {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
padding: 10px 25px;
font-size: 1.1rem;
font-weight: bold;
background-color: #f2e6c9;
border: 2px solid #000;
border-radius: 8px;
cursor: pointer;
box-shadow: 2px 2px 0 #000;
transition: transform 0.2s ease;
z-index: 9999; /* pour quil soit toujours au-dessus */
}
.btn-retour:hover {
transform: translateX(-50%) scale(1.05);
background-color: #e0d1b3;
}

@ -38,31 +38,33 @@ class PopulateDBEmojiAvailableCommand extends Command
$this->connection->executeStatement(' $this->connection->executeStatement('
CREATE TABLE stock_emoji ( CREATE TABLE stock_emoji (
id INT PRIMARY KEY, id INT PRIMARY KEY,
code VARCHAR(255) NOT NULL code VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL
) )
'); ');
// On peuple la table // On peuple la table
$this->connection->executeStatement("INSERT INTO stock_emoji (id, code) VALUES $this->connection->executeStatement("INSERT INTO stock_emoji (id, code, name) VALUES
(1,'🤖'), (1,'🤖','Rodolph'),
(2,'😺'), (2,'😺','Bobette'),
(3,'🧠'), (3,'🧠','Diana'),
(4,'👻'), (4,'👻','Ian'),
(5,'🧟'), (5,'🧟','Alice'),
(6,'🐶'), (6,'🐶','Eric'),
(7,'👽'), (7,'👽','Hannah'),
(8,'🧛'), (8,'🧛','Fiona'),
(9,'🎃'), (9,'🎃','George'),
(10,'🐸'), (10,'🐸','John'),
(11,'⚡'), (11,'⚡','Charlie'),
(12,'💀'), (12,'💀','Benoit'),
(13,'🔥'), (13,'🔥','Sophie'),
(14,'🧙'), (14,'🧙','Bob'),
(15,'🌪️'), (15,'🌪️','Ethan'),
(16,'😎'), (16,'😎','Luna'),
(17,'😁'), (17,'😁','Maxence'),
(18,'🌟'), (18,'🌟','Jasper'),
(19,'😈') (19,'😈','Nora')
"); ");
$output->writeln('Base de données peuplée.'); $output->writeln('Base de données peuplée.');

@ -0,0 +1,98 @@
<?php
namespace App\Controller;
use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
#[Route('/admin', name: 'admin_')]
#[IsGranted('ROLE_ADMIN')]
class AdminController extends AbstractController
{
private EntityManagerInterface $entityManager;
private UserPasswordHasherInterface $passwordHasher;
public function __construct(EntityManagerInterface $entityManager, UserPasswordHasherInterface $passwordHasher)
{
$this->entityManager = $entityManager;
$this->passwordHasher = $passwordHasher;
}
#[Route('', name: 'admin_dashboard')]
public function dashboard(): Response
{
return $this->render('admin/index.html.twig');
}
#[Route('/users', name: 'users_list', methods: ['GET'])]
public function getUserById(UserRepository $userRepository): JsonResponse
{
$users = $userRepository->findAll();
$data = array_map(function (User $user) {
return [
'id' => $user->getId(),
'username' => $user->getUsername(),
'roles' => $user->getRoles(),
];
}, $users);
return $this->json($data);
}
#[Route('/users/add', name: 'add_user', methods: ['POST'])]
public function addUser(Request $request, UserPasswordHasherInterface $passwordHasher): JsonResponse
{
$data = json_decode($request->getContent(), true);
$username = $data['username'] ?? null;
$password = $data['password'] ?? null;
$roles = $data['roles'] ?? ['ROLE_USER'];
if (!$username || !$password) {
return $this->json(['error' => 'Missing username or password'], Response::HTTP_BAD_REQUEST);
}
$existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['username' => $username]);
if ($existingUser) {
return $this->json(['error' => 'User already exists'], Response::HTTP_CONFLICT);
}
$user = new User();
$user->setUsername($username);
$user->setRoles($roles);
$user->setPassword($passwordHasher->hashPassword($user, $password));
$this->entityManager->persist($user);
$this->entityManager->flush();
return $this->json(['message' => 'User created successfully', 'id' => $user->getId()], Response::HTTP_CREATED);
}
#[Route('/users/delete/{id}', name: 'delete_user', methods: ['DELETE'])]
public function deleteUser(int $id): JsonResponse
{
$user = $this->entityManager->getRepository(User::class)->find($id);
if (!$user) {
return $this->json(['error' => 'User not found'], Response::HTTP_NOT_FOUND);
}
foreach ($user->getEmojis() as $userEmoji) {
$this->entityManager->remove($userEmoji);
}
$this->entityManager->remove($user);
$this->entityManager->flush();
return $this->json(['message' => 'User and related data deleted successfully'], Response::HTTP_OK);
}
}

@ -0,0 +1,45 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class CombatController extends AbstractController
{
// Route designed for testing purposes
#[Route('/combat', name: 'app_combat')]
public function index(): Response
{
$leftEmoji = '🐱';
$rightEmoji = '🐶';
$winner = 'left'; // or 'right'
$loser = ($winner === 'left') ? 'right' : 'left';
return $this->render('combat/index.html.twig', [
'left_emoji' => $leftEmoji,
'right_emoji' => $rightEmoji,
'winner' => $winner,
'loser' => $loser,
]);
}
private function isEmoji(string $str): bool {
$emojiRegex = '/^\X$/u';
return preg_match($emojiRegex, $str) === 1 && mb_strlen($str, 'UTF-8') === 1;
}
#[Route('/combat/{idLeft}/{idRight}', name: 'app_combat_with_params', requirements: ['leftEmoji' => '.+', 'rightEmoji' => '.+'])]
public function combatWithParams(string $idLeft, string $idRight): Response
// Example : /combat-🐱-🐶-left
// or /combat-🐌_☀%EF%B8%8F-🐌_☀%EF%B8%8F-left
{
return $this->render('combat/index.html.twig', [
'idLeft' => $idLeft,
'idRight' => $idRight,
]);
}
}

@ -3,6 +3,7 @@ namespace App\Controller;
use App\Entity\Emoji; use App\Entity\Emoji;
use App\Entity\Rarity; use App\Entity\Rarity;
use App\Service\EmojiService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -12,89 +13,40 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use App\Repository\EmojiRepository; use App\Repository\EmojiRepository;
use App\Entity\UserEmojis;
use App\Repository\UserRepository;
use App\Repository\UserEmojisRepository;
use App\Entity\StockEmoji;
#[Route('/emoji', name: 'app_emoji')] #[Route('/emojis', name: 'app_emoji_')]
class EmojiController extends AbstractController class EmojiController extends AbstractController
{ {
private RarityRepository $rarityRepository; private RarityRepository $rarityRepository;
private HttpClientInterface $httpClient; private HttpClientInterface $httpClient;
private EmojiService $emojiService;
private UserRepository $userRepository;
private UserEmojisRepository $userEmojisRepository;
private EntityManagerInterface $em;
public function __construct(RarityRepository $rarityRepository, HttpClientInterface $httpClient) public function __construct(RarityRepository $rarityRepository, HttpClientInterface $httpClient, EmojiService $emojiService,
UserRepository $userRepository, UserEmojisRepository $userEmojisRepository, EntityManagerInterface $em)
{ {
$this->rarityRepository = $rarityRepository; $this->rarityRepository = $rarityRepository;
$this->httpClient = $httpClient; $this->httpClient = $httpClient;
$this->emojiService = $emojiService;
$this->userRepository = $userRepository;
$this->userEmojisRepository = $userEmojisRepository;
$this->em = $em;
} }
#[Route('/emoji', name: 'app_emoji')] #[Route('/', name: 'emojis')]
public function index(): Response public function index(): Response
{ {
$this->testToMove();
return $this->render('emoji/index.html.twig', [ return $this->render('emoji/index.html.twig', [
'controller_name' => 'EmojiController', 'controller_name' => 'EmojiController',
]); ]);
} }
#[Route('/count', name: 'count')]
public function count(EmojiRepository $emojiRepository): Response
{
$count = count($emojiRepository->findAll());
return new Response(['count' => $count]);
}
#[Route('/addRarity', name: 'addR')]
public function addRarityDebug(EntityManagerInterface $entityManager) {
$rarity = new Rarity();
$rarity->setName('Bip');
$rarity->setDropRate(42);
$entityManager->persist($rarity);
$entityManager->flush();
return new Response();
}
#[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 // 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 private function getFusionUrl(string $emoji1, string $emoji2): string
{ {
@ -108,8 +60,15 @@ class EmojiController extends AbstractController
return sprintf('%s%s_%s?size=%d', $baseUrl, $encodedEmoji1, $encodedEmoji2, $size); return sprintf('%s%s_%s?size=%d', $baseUrl, $encodedEmoji1, $encodedEmoji2, $size);
} }
#[Route('/fusion/{emoji1_id}/{emoji2_id}', name: 'fusion')] #[Route('/fusion/{userId}/{emoji1_id}/{emoji2_id}', name: 'fusion', methods: ['GET'], requirements: ['userId' => '\d+', 'emoji1_id' => '\d+', 'emoji2_id' => '\d+'])]
public function reproduceEmoji(int $emoji1_id, int $emoji2_id, EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse { public function reproduceEmoji(int $userId, int $emoji1_id, int $emoji2_id, EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse {
$user = $this->userRepository->findOneBy(['id' => $userId]);
if (!$user) {
return new JsonResponse(['error' => 'User not found'], 404);
}
$emoji1 = $emojiRepository->find($emoji1_id); $emoji1 = $emojiRepository->find($emoji1_id);
$emoji2 = $emojiRepository->find($emoji2_id); $emoji2 = $emojiRepository->find($emoji2_id);
@ -119,11 +78,24 @@ class EmojiController extends AbstractController
$child = new Emoji(); $child = new Emoji();
$child->setName("Testenfant"); $names = [
'Zabbo', 'Snorky', 'Wiblo', 'Kipzi', 'Gribz', 'Flimbo', 'Plinko', 'Raffu', 'Mibzo', 'Dazzo',
'Flinko', 'Groopi', 'Zimba', 'Wocky', 'Trizzi', 'Lumpo', 'Yabbo', 'Glinky', 'Zuffo', 'Brimbo',
'Chompa', 'Snizzle', 'Wibzi', 'Paffy', 'Tizzle', 'Nokko', 'Lazzo', 'Flumzi', 'Wozzy', 'Blimpo',
'Zuppi', 'Glinty', 'Fobzi', 'Muggo', 'Twibzy', 'Crungo', 'Jibbi', 'Snoffy', 'Glimzo', 'Daffo',
'Nibzi', 'Kranko', 'Yibba', 'Bloopo', 'Taffli', 'Zagga', 'Whizzo', 'Plobbi', 'Ruffli', 'Snibbo',
'Fuzzu', 'Gozzi', 'Trabbo', 'Flonzi', 'Whimpy', 'Klipzo', 'Zombzi', 'Hiffa', 'Fruggo', 'Luzzli',
'Zilpo', 'Baffi', 'Ramboo', 'Dibzo', 'Muffli', 'Kaffo', 'Gobzi', 'Tobbu', 'Xibzi', 'Pufflo',
'Jabbo', 'Zinky', 'Baffro', 'Tizzi', 'Wombo', 'Snuxi', 'Lompa', 'Criffy', 'Mebzi', 'Zoffo',
'Quimbo', 'Trubzi', 'Hozzy', 'Kabbo', 'Snooki', 'Laffo', 'Glumpi', 'Vibzo', 'Druffo', 'Whirli',
'Torko', 'Gropzi', 'Plibzo', 'Shaffi', 'Zabboz', 'Wubzi', 'Cloppo', 'Nibzo', 'Flunzi', 'Krabbo'
];
$randomName = $names[array_rand($names)];
$child->setName($randomName);
// Chance de fusion // Chance de fusion
$fusionRand = mt_rand() / mt_getrandmax(); $fusionRand = mt_rand() / mt_getrandmax();
if($fusionRand > 0.8) { if($fusionRand > 0.5) {
// Si les emoji parents sont des fusions on remonte l'arbre généalogique jusqu'à trouver un smiley normal // 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é // On ne peut pas fusionner un smiley déjà fusionné
$temp1 = $emoji1; $temp1 = $emoji1;
@ -138,12 +110,11 @@ class EmojiController extends AbstractController
$child->setCode($this->getFusionUrl($temp1->getCode(), $temp2->getCode())); $child->setCode($this->getFusionUrl($temp1->getCode(), $temp2->getCode()));
} else { } else {
$rand = mt_rand() / mt_getrandmax(); $repository = $this->em->getRepository(StockEmoji::class);
if($rand > 0.5) { $allEmojis = $repository->findAll();
$child->setCode($emoji1->getCode()); $randomKey = array_rand($allEmojis);
} else { $randomEmoji = $allEmojis[$randomKey]->getCode();
$child->setCode($emoji2->getCode()); $child->setCode($randomEmoji);
}
} }
// Lors d'une fusion un emoji récupère chaque stat d'un de ces deux parents de manière aléatoire // Lors d'une fusion un emoji récupère chaque stat d'un de ces deux parents de manière aléatoire
@ -177,28 +148,33 @@ class EmojiController extends AbstractController
$child->setFightsWon(0); $child->setFightsWon(0);
$child->setRarity($this->getRarity()); $child->setRarity($this->emojiService->generateRarity());
$child->setParent1($emoji1); $child->setParent1($emoji1);
$child->setParent2($emoji2); $child->setParent2($emoji2);
$entityManager->persist($child); $entityManager->persist($child);
$userEmoji = new UserEmojis();
$userEmoji->setUser($user);
$userEmoji->setEmoji($child);
$entityManager->persist($userEmoji);
$entityManager->flush(); $entityManager->flush();
return new JsonResponse([ return new JsonResponse([
'message' => 'Child created', 'message' => 'Child created',
'childId' => $child->getId() 'mommy' => $child->getParent1()->getCode(),
'daddy' => $child->getParent2()->getCode(),
'baby' => $child->getCode()
]); ]);
} }
#[Route('/startEmoji', name: 'get_starter_emoji')]
public function startEmoji(EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse {
}
#[Route('/fight/{idEmoji1}/{idEmoji2}', name: 'fight_emoji')] #[Route('/fight/{idEmoji1}/{idEmoji2}', name: 'fight_emoji', methods: ['GET'], requirements: ['idEmoji1' => '\d+', 'idEmoji2' => '\d+'])]
public function fightEmoji(int $idEmoji1, int $idEmoji2, EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse { public function fightEmoji(int $idEmoji1, int $idEmoji2, EntityManagerInterface $entityManager, EmojiRepository $emojiRepository): JsonResponse
{
$emoji1 = $emojiRepository->find($idEmoji1); $emoji1 = $emojiRepository->find($idEmoji1);
$emoji2 = $emojiRepository->find($idEmoji2); $emoji2 = $emojiRepository->find($idEmoji2);
@ -209,37 +185,64 @@ class EmojiController extends AbstractController
$valEmoji1 = [$emoji1->getStrength(),$emoji1->getToughness(),$emoji1->getIntelligence(),$emoji1->getSpeed()]; $valEmoji1 = [$emoji1->getStrength(),$emoji1->getToughness(),$emoji1->getIntelligence(),$emoji1->getSpeed()];
$valEmoji2 = [$emoji2->getStrength(),$emoji2->getToughness(),$emoji2->getIntelligence(),$emoji2->getSpeed()]; $valEmoji2 = [$emoji2->getStrength(),$emoji2->getToughness(),$emoji2->getIntelligence(),$emoji2->getSpeed()];
$difference = $valEmoji1[$aleatoire] - $valEmoji2[$aleatoire]; $difference = $valEmoji1[$aleatoire] - $valEmoji2[$aleatoire];
if($difference > 0){
$emoji1->wonFight(); if ($difference > 0) {
$entityManager->persist($emoji1); $winner = $emoji1;
$entityManager->remove($emoji2); $loser = $emoji2;
$wonFight = 'left';
} else { } else {
$emoji2->wonFight(); $winner = $emoji2;
$entityManager->persist($emoji2); $loser = $emoji1;
$entityManager->remove($emoji1); $wonFight = 'right';
} }
$winner->wonFight();
$userEmojiLoser = $this->userEmojisRepository->findOneBy(['emoji' => $loser]);
$entityManager->persist($winner);
$entityManager->remove($loser);
$entityManager->remove($userEmojiLoser);
$entityManager->flush(); $entityManager->flush();
return new JsonResponse([ return new JsonResponse([
'message' => 'End of the fight', 'leftEmoji' => $emoji1->getCode(),
'rightEmoji' => $emoji2->getCode(),
'winner' => $wonFight
]); ]);
} }
public function testToMove(){ #[Route('/{emojiID}/getParents/', name: 'get_parents_emoji', methods: ['GET'])]
$e = new Emoji(); public function getParentsEmoji(int $emojiID, EmojiRepository $emojiRepository): JsonResponse {
$e->setName("ROBERT"); $emoji = $emojiRepository->find($emojiID);
$e->setStrength(5);
$e->setIntelligence(2); if (!$emoji) {
$e->setToughness(3); return new JsonResponse(['error' => 'Emoji not found'], 404);
$e->setSpeed(4); }
$e2 = new Emoji();
$e2->setName("BIBOP"); $codeParent1 = null;
$e2->setStrength(42); $codeParent2 = null;
$e2->setIntelligence(1); $nameParent1 = 'inconnu';
$e2->setToughness(1); $nameParent2 = 'inconnu';
$e2->setSpeed(1);
$vic = $this->fightEmoji($e,$e2);
echo $vic->getName(); if ($emoji->getParent1()) {
$codeParent1 = $emoji->getParent1()->getCode();
$nameParent1 = $emoji->getParent1()->getName();
}
if ($emoji->getParent2()) {
$codeParent2 = $emoji->getParent2()->getCode();
$nameParent2 = $emoji->getParent2()->getName();
}
return new JsonResponse([
'codeParent1' => $codeParent1,
'nameParent1' => $nameParent1,
'codeParent2' => $codeParent2,
'nameParent2' => $nameParent2,
'codeEnfant' => $emoji->getCode(),
'nameEnfant' => $emoji->getName(),
]);
} }
} }

@ -2,188 +2,38 @@
namespace App\Controller; namespace App\Controller;
use App\Service\EmojiService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController class HomeController extends AbstractController
{ {
private EmojiService $emojiService;
public function __construct(EmojiService $emojiService)
{
$this->emojiService = $emojiService;
}
#[Route('/', name: 'app_home')] #[Route('/', name: 'app_home')]
public function index(): Response public function index(): Response
{ {
$emojisDeBase = [ $user = $this->getUser();
[
'id' => 1,
'nom' => 'Bob',
'code' => '😊',
'force' => 12.5,
'robustesse' => 9.3,
'intelligence' => 7.8,
'vitesse' => 10.0,
'nbCombatGagne' => 3,
'rarete' => 3, // é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' => 5, // légendaire
],
[
'id' => 4,
'nom' => 'Sophie',
'code' => '😎',
'force' => 8.0,
'robustesse' => 7.5,
'intelligence' => 9.0,
'vitesse' => 8.5,
'nbCombatGagne' => 2,
'rarete' => 4, // mythique
]
];
$emojisCrees = [ if (!$user) {
[ return $this->redirectToRoute('app_login');
'id' => 6, }
'nom' => 'Benoit',
'code' => '🤖', // Récupération des emojis
'force' => 15.0, $allEmojis = $this->emojiService->getEmojisByUser($user);
'robustesse' => 12.0,
'intelligence' => 14.0, // Séparation : 4 premiers en base, le reste en créés
'vitesse' => 13.0, $emojisDeBase = array_slice($allEmojis, 0, 4);
'nbCombatGagne' => 5, $emojisCrees = array_slice($allEmojis, 4);
'rarete' => 3, // épique
],
[
'id' => 7,
'nom' => 'Eric',
'code' => '🌟',
'force' => 18.0,
'robustesse' => 16.0,
'intelligence' => 17.0,
'vitesse' => 19.0,
'nbCombatGagne' => 8,
'rarete' => 5, // légendaire
],
[
'id' => 5,
'nom' => 'Alice',
'code' => '🥳',
'force' => 6.0,
'robustesse' => 5.0,
'intelligence' => 4.5,
'vitesse' => 6.5,
'nbCombatGagne' => 0,
'rarete' => 2, // rare
],
[
'id' => 8,
'nom' => 'Bobette',
'code' => '🤩',
'force' => 10.0,
'robustesse' => 9.0,
'intelligence' => 11.0,
'vitesse' => 12.0,
'nbCombatGagne' => 4,
'rarete' => 4, // mythique
],
[
'id' => 9,
'nom' => 'Charlie',
'code' => '😇',
'force' => 7.0,
'robustesse' => 6.0,
'intelligence' => 8.0,
'vitesse' => 7.5,
'nbCombatGagne' => 2,
'rarete' => 1, // commun
],
[
'id' => 10,
'nom' => 'Diana',
'code' => '😈',
'force' => 14.0,
'robustesse' => 13.0,
'intelligence' => 15.0,
'vitesse' => 16.0,
'nbCombatGagne' => 6,
'rarete' => 4, // mythique
],
[
'id' => 11,
'nom' => 'Ethan',
'code' => '🤯',
'force' => 9.0,
'robustesse' => 8.0,
'intelligence' => 10.0,
'vitesse' => 11.0,
'nbCombatGagne' => 3,
'rarete' => 3, // épique
],
[
'id' => 12,
'nom' => 'Fiona',
'code' => '🥺',
'force' => 4.0,
'robustesse' => 3.5,
'intelligence' => 5.0,
'vitesse' => 4.5,
'nbCombatGagne' => 1,
'rarete' => 1, // commun
],
[
'id' => 13,
'nom' => 'George',
'code' => '😜',
'force' => 11.0,
'robustesse' => 10.0,
'intelligence' => 12.0,
'vitesse' => 13.5,
'nbCombatGagne' => 2,
'rarete' => 3, // épique
],
[
'id' => 14,
'nom' => 'Hannah',
'code' => '😏',
'force' => 3.0,
'robustesse' => 2.5,
'intelligence' => 4.0,
'vitesse' => 3.5,
'nbCombatGagne' => 0,
'rarete' => 2, // rare
],
[
'id' => 15,
'nom' => 'Ian',
'code' => '😬',
'force' => 17.0,
'robustesse' => 14.0,
'intelligence' => 16.0,
'vitesse' => 18.0,
'nbCombatGagne' => 7,
'rarete' => 5, // légendaire
],
];
foreach ($emojisDeBase as &$emoji) { foreach ($emojisDeBase as &$emoji) {
$emoji['color'] = match ($emoji['rarete']) { $emoji['color'] = match ($emoji['rarity']) {
2 => 'green', 2 => 'green',
3 => 'purple', 3 => 'purple',
4 => 'red', 4 => 'red',
@ -193,7 +43,7 @@ class HomeController extends AbstractController
} }
foreach ($emojisCrees as &$emoji) { foreach ($emojisCrees as &$emoji) {
$emoji['color'] = match ($emoji['rarete']) { $emoji['color'] = match ($emoji['rarity']) {
2 => 'green', 2 => 'green',
3 => 'purple', 3 => 'purple',
4 => 'red', 4 => 'red',
@ -202,10 +52,10 @@ class HomeController extends AbstractController
}; };
} }
return $this->render('home/index.html.twig', [ return $this->render('home/index.html.twig', [
'user' => $user,
'emojisDeBase' => $emojisDeBase, 'emojisDeBase' => $emojisDeBase,
'emojisCrees' => $emojisCrees, 'emojisCrees' => $emojisCrees
]); ]);
} }
} }

@ -2,6 +2,8 @@
namespace App\Controller; namespace App\Controller;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
@ -9,16 +11,40 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class LoginController extends AbstractController class LoginController extends AbstractController
{ {
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
#[Route(path: '/login', name: 'app_login')] #[Route(path: '/login', name: 'app_login')]
public function login(AuthenticationUtils $authenticationUtils): Response public function login(AuthenticationUtils $authenticationUtils): Response
{ {
if ($this->getUser()) { if ($this->getUser()) {
return $this->redirectToRoute('app_home'); $user = $this->getUser();
$emojis = $user->getEmojis()->map(function ($userEmoji) {
$emoji = $userEmoji->getEmoji();
return [
'id' => $emoji->getId(),
'name' => $emoji->getName(),
'code' => $emoji->getCode(),
];
})->toArray();
return $this->render('home/index.html.twig', [
'user' => $user,
'emojis' => $emojis
]);
} }
$error = $authenticationUtils->getLastAuthenticationError(); $error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername(); $lastUsername = $authenticationUtils->getLastUsername();
return $this->render('auth/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]); return $this->render('auth/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error
]);
} }
#[Route(path: '/logout', name: 'app_logout')] #[Route(path: '/logout', name: 'app_logout')]

@ -5,6 +5,7 @@ namespace App\Controller;
use App\Entity\User; use App\Entity\User;
use App\Form\RegistrationFormType; use App\Form\RegistrationFormType;
use App\Security\LoginFormAuthenticator; use App\Security\LoginFormAuthenticator;
use App\Service\EmojiService;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -16,8 +17,14 @@ use Symfony\Component\Security\Http\Authentication\UserAuthenticatorInterface;
class RegistrationController extends AbstractController class RegistrationController extends AbstractController
{ {
#[Route('/register', name: 'app_register')] #[Route('/register', name: 'app_register')]
public function register(Request $request, UserPasswordHasherInterface $userPasswordHasher, UserAuthenticatorInterface $userAuthenticator, LoginFormAuthenticator $authenticator, EntityManagerInterface $entityManager): Response public function register(
{ Request $request,
UserPasswordHasherInterface $userPasswordHasher,
UserAuthenticatorInterface $userAuthenticator,
LoginFormAuthenticator $authenticator,
EntityManagerInterface $entityManager,
EmojiService $emojiService
): Response {
$user = new User(); $user = new User();
$form = $this->createForm(RegistrationFormType::class, $user); $form = $this->createForm(RegistrationFormType::class, $user);
$form->handleRequest($request); $form->handleRequest($request);
@ -34,6 +41,8 @@ class RegistrationController extends AbstractController
$entityManager->persist($user); $entityManager->persist($user);
$entityManager->flush(); $entityManager->flush();
$emojiService->addRandomStockEmojisToUser($user);
return $userAuthenticator->authenticateUser( return $userAuthenticator->authenticateUser(
$user, $user,
$authenticator, $authenticator,

@ -0,0 +1,37 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ReproductionController extends AbstractController
{
// #[Route('/reproduction', name: 'app_reproduction')]
// public function index(): Response
// {
// $leftEmoji = '🐶'; // Emoji de gauche
// $rightEmoji = '🐱'; // Emoji de droite
// $childEmoji = 'https://emojik.vercel.app/s/%F0%9F%90%88_%F0%9F%90%95'; // Emoji enfant
// // $childEmoji = '🐱';
// return $this->render('reproduction/index.html.twig', [
// 'left_emoji' => $leftEmoji,
// 'right_emoji' => $rightEmoji,
// 'child_emoji' => $childEmoji
// ]);
// }
#[Route('/reproduction/{idUser}/{id1}/{id2}', name: 'app_reproduction_with_params')]
public function reproductionWithParams(int $idUser, int $id1, int $id2): Response
{
return $this->render('reproduction/index.html.twig', [
'idUser' => $idUser,
'left_emoji' => $id1,
'right_emoji' => $id2,
]);
}
}

@ -0,0 +1,22 @@
<?php
namespace App\Controller;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\ORM\EntityManagerInterface;
#[Route('/users', name: 'users_')]
class UserController extends AbstractController
{
private EntityManagerInterface $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
}

@ -4,8 +4,10 @@ namespace App\Entity;
use App\Repository\EmojiRepository; use App\Repository\EmojiRepository;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Metadata\ApiResource;
#[ORM\Entity(repositoryClass: EmojiRepository::class)] #[ORM\Entity(repositoryClass: EmojiRepository::class)]
#[ApiResource]
class Emoji class Emoji
{ {
#[ORM\Id] #[ORM\Id]

@ -4,8 +4,10 @@ namespace App\Entity;
use App\Repository\RarityRepository; use App\Repository\RarityRepository;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Metadata\ApiResource;
#[ORM\Entity(repositoryClass: RarityRepository::class)] #[ORM\Entity(repositoryClass: RarityRepository::class)]
#[ApiResource]
class Rarity class Rarity
{ {
#[ORM\Id] #[ORM\Id]

@ -4,8 +4,10 @@ namespace App\Entity;
use App\Repository\StockEmojiRepository; use App\Repository\StockEmojiRepository;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use ApiPlatform\Metadata\ApiResource;
#[ORM\Entity(repositoryClass: StockEmojiRepository::class)] #[ORM\Entity(repositoryClass: StockEmojiRepository::class)]
#[ApiResource]
class StockEmoji class StockEmoji
{ {
#[ORM\Id] #[ORM\Id]
@ -16,6 +18,9 @@ class StockEmoji
#[ORM\Column(length: 255)] #[ORM\Column(length: 255)]
private ?string $code = null; private ?string $code = null;
#[ORM\Column(length: 255)]
private ?string $name = null;
public function getId(): ?int public function getId(): ?int
{ {
return $this->id; return $this->id;
@ -32,4 +37,16 @@ class StockEmoji
return $this; return $this;
} }
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
} }

@ -9,9 +9,11 @@ use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use ApiPlatform\Metadata\ApiResource;
#[ORM\Entity(repositoryClass: UserRepository::class)] #[ORM\Entity(repositoryClass: UserRepository::class)]
#[UniqueEntity(fields: ['username'], message: 'There is already an account with this username')] #[UniqueEntity(fields: ['username'], message: 'Un compte existe déjà avec ce nom d\'utilisateur')]
#[ApiResource]
class User implements UserInterface, PasswordAuthenticatedUserInterface class User implements UserInterface, PasswordAuthenticatedUserInterface
{ {
#[ORM\Id] #[ORM\Id]
@ -31,8 +33,7 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
#[ORM\Column] #[ORM\Column]
private ?string $password = null; private ?string $password = null;
#[ORM\ManyToMany(targetEntity: Emoji::class, inversedBy: 'users')] #[ORM\OneToMany(targetEntity: UserEmojis::class, mappedBy: 'user')]
#[ORM\JoinTable(name: 'posseder')]
private Collection $emojis; private Collection $emojis;
public function __construct() { public function __construct() {
@ -56,19 +57,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
return $this; return $this;
} }
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string public function getUserIdentifier(): string
{ {
return (string) $this->username; return (string) $this->username;
} }
/**
* @see UserInterface
*/
public function getRoles(): array public function getRoles(): array
{ {
$roles = $this->roles; $roles = $this->roles;
@ -85,9 +79,6 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
return $this; return $this;
} }
/**
* @see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string public function getPassword(): string
{ {
return $this->password; return $this->password;
@ -101,9 +92,9 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface
} }
public function getEmojis(): Collection public function getEmojis(): Collection
{ {
return $this->emojis; return $this->emojis;
} }
/** /**
* @see UserInterface * @see UserInterface

@ -0,0 +1,52 @@
<?php
namespace App\Entity;
use App\Repository\UserEmojisRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: UserEmojisRepository::class)]
class UserEmojis
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(nullable: false)]
private ?User $user = null;
#[ORM\ManyToOne(targetEntity: Emoji::class)]
#[ORM\JoinColumn(nullable: false)]
private ?Emoji $emoji = null;
public function getId(): ?int
{
return $this->id;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): static
{
$this->user = $user;
return $this;
}
public function getEmoji(): ?Emoji
{
return $this->emoji;
}
public function setEmoji(?Emoji $emoji): static
{
$this->emoji = $emoji;
return $this;
}
}

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

@ -0,0 +1,104 @@
<?php
namespace App\Service;
use App\Entity\Emoji;
use App\Entity\User;
use App\Entity\UserEmojis;
use App\Entity\Rarity;
use App\Repository\StockEmojiRepository;
use App\Repository\RarityRepository;
use Doctrine\ORM\EntityManagerInterface;
class EmojiService
{
private $entityManager;
private $stockEmojiRepository;
private $rarityRepository;
public function __construct(
EntityManagerInterface $entityManager,
StockEmojiRepository $stockEmojiRepository,
RarityRepository $rarityRepository
) {
$this->entityManager = $entityManager;
$this->stockEmojiRepository = $stockEmojiRepository;
$this->rarityRepository = $rarityRepository;
}
public function addRandomStockEmojisToUser(User $user, int $numberOfEmojis = 4): void
{
$stockEmojis = $this->stockEmojiRepository->findAll();
if (count($stockEmojis) < $numberOfEmojis) {
throw new \RuntimeException('Not enough emojis in stock.');
}
$randomEmojis = $this->getRandomEmojis($stockEmojis, $numberOfEmojis);
foreach ($randomEmojis as $stockEmoji) {
$emoji = new Emoji();
$emoji->setName($stockEmoji->getName());
$emoji->setCode($stockEmoji->getCode());
$emoji->setStrength(mt_rand(0, 10));
$emoji->setToughness(mt_rand(0, 10));
$emoji->setIntelligence(mt_rand(0, 10));
$emoji->setSpeed(mt_rand(0, 10));
$emoji->setFightsWon(0);
// Set the rarity
$rarity = $this->generateRarity();
$emoji->setRarity($rarity);
$this->entityManager->persist($emoji);
$userEmoji = new UserEmojis();
$userEmoji->setUser($user);
$userEmoji->setEmoji($emoji);
$this->entityManager->persist($userEmoji);
}
$this->entityManager->flush();
}
private function getRandomEmojis(array $stockEmojis, int $count): array
{
shuffle($stockEmojis);
return array_slice($stockEmojis, 0, $count);
}
public function generateRarity(): Rarity
{
$rarities = $this->rarityRepository->findAll();
$rand = mt_rand() / mt_getrandmax();
$sum = 0.0;
foreach ($rarities as $rarity) {
$sum += $rarity->getDropRate();
if ($sum > $rand) {
return $rarity;
}
}
return $rarities[0];
}
public function getEmojisByUser(User $user): array
{
return $user->getEmojis()->map(function ($userEmoji) {
$emoji = $userEmoji->getEmoji();
return [
'id' => $emoji->getId(),
'name' => $emoji->getName(),
'code' => $emoji->getCode(),
'strength' => $emoji->getStrength(),
'toughness' => $emoji->getToughness(),
'intelligence' => $emoji->getIntelligence(),
'speed' => $emoji->getSpeed(),
'fightsWon' => $emoji->getFightsWon(),
'rarity' => $emoji->getRarity() ? $emoji->getRarity()->getId() : 0,
];
})->toArray();
}
}

@ -8,9 +8,9 @@
"ref": "696d44adc3c0d4f5d25a2f1c4f3700dd8a5c6db9" "ref": "696d44adc3c0d4f5d25a2f1c4f3700dd8a5c6db9"
}, },
"files": [ "files": [
"./config/packages/api_platform.yaml", "config/packages/api_platform.yaml",
"./config/routes/api_platform.yaml", "config/routes/api_platform.yaml",
"./src/ApiResource/.gitignore" "src/ApiResource/.gitignore"
] ]
}, },
"doctrine/deprecations": { "doctrine/deprecations": {
@ -58,7 +58,7 @@
"ref": "6bea22e6c564fba3a1391615cada1437d0bde39c" "ref": "6bea22e6c564fba3a1391615cada1437d0bde39c"
}, },
"files": [ "files": [
"./config/packages/nelmio_cors.yaml" "config/packages/nelmio_cors.yaml"
] ]
}, },
"phpunit/phpunit": { "phpunit/phpunit": {

@ -0,0 +1,84 @@
{% extends 'base.html.twig' %}
{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('css/admin.css') }}">
{% endblock %}
{% block title %}Admin - Gestion des utilisateurs{% endblock %}
{% block body %}
<h1>🔐 Admin - Gestion des utilisateurs</h1>
<section>
<h2>👥 Liste des utilisateurs</h2>
<ul id="user-list"></ul>
</section>
<section>
<h2> Ajouter un utilisateur</h2>
<form id="add-user-form">
<input type="text" name="username" placeholder="Nom d'utilisateur" required>
<input type="password" name="password" placeholder="Mot de passe" required>
<select name="role">
<option value="ROLE_USER">Utilisateur</option>
<option value="ROLE_ADMIN">Administrateur</option>
</select>
<button type="submit">Ajouter</button>
</form>
</section>
<script>
async function fetchUsers() {
const response = await fetch('/admin/users');
const users = await response.json();
const list = document.getElementById('user-list');
list.innerHTML = '';
users.forEach(user => {
const item = document.createElement('li');
item.innerHTML = `
<strong>${user.username}</strong> - Rôles: ${user.roles.join(', ')}
<button onclick="deleteUser(${user.id})">🗑️ Supprimer</button>
`;
list.appendChild(item);
});
}
async function deleteUser(id) {
if (!confirm('Confirmer la suppression de cet utilisateur ?')) return;
const response = await fetch(`/admin/users/delete/${id}`, {
method: 'DELETE',
});
if (response.ok) {
alert('Utilisateur supprimé.');
fetchUsers();
} else {
alert('Erreur lors de la suppression.');
}
}
document.getElementById('add-user-form').addEventListener('submit', async function (e) {
e.preventDefault();
const form = e.target;
const username = form.username.value;
const password = form.password.value;
const role = form.role.value;
const response = await fetch('/admin/users/add', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password, roles: [role] }),
});
if (response.ok) {
alert('Utilisateur ajouté avec succès.');
form.reset();
fetchUsers();
} else {
const data = await response.json();
alert('Erreur: ' + (data.error || 'Inconnue'));
}
});
fetchUsers();
</script>
{% endblock %}

@ -13,7 +13,7 @@
<h1 class="login-title">🔐 Connexion</h1> <h1 class="login-title">🔐 Connexion</h1>
{% if error %} {% if error %}
<div class="login-error">{{ error.messageKey|trans(error.messageData, 'security') }}</div> <div class="login-error">Mauvais nom d'utilisateur ou mot de passe.</div>
{% endif %} {% endif %}
{% if app.user %} {% if app.user %}

@ -4,14 +4,14 @@
{% block stylesheets %} {% block stylesheets %}
{{ parent() }} {{ parent() }}
<link rel="stylesheet" href="{{ asset('css/login.css') }}"> <link rel="stylesheet" href="{{ asset('css/register.css') }}">
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<div class="register-container"> <div class="register-container">
<h1>📝 Inscription</h1> <h1 class="register-title">📝 Inscription</h1>
{{ form_start(registrationForm) }} {{ form_start(registrationForm, {'attr': {'class': 'register-form'}}) }}
{{ form_errors(registrationForm) }} {{ form_errors(registrationForm) }}
{{ form_row(registrationForm.username) }} {{ form_row(registrationForm.username) }}
@ -19,12 +19,12 @@
label: 'Mot de passe' label: 'Mot de passe'
}) }} }) }}
<button type="submit" class="btn">Créer mon compte</button> <button type="submit" class="btn-register-submit">Créer mon compte</button>
{{ form_end(registrationForm) }}
<div class="no-account"> <div class="already-account">
<p>Déjà inscrit ?</p> <p>Déjà inscrit ?</p>
<a href="{{ path('app_login') }}" class="btn-register">Se connecter</a> <a href="{{ path('app_login') }}" class="btn-login-link">Se connecter</a>
</div> </div>
{{ form_end(registrationForm) }}
</div> </div>
{% endblock %} {% endblock %}

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Emoji Fight</title>
<link rel="stylesheet" href="{{ asset('style/combat.css?v=1.5') }}" />
</head>
<body>
<div class="arena">
<div class="emoji left-emoji" data-content=""></div>
<div class="emoji right-emoji" data-content=""></div>
</div>
<div>
<button id="btn-retour" class="btn-retour">🏠 Retour à la collection</button>
</div>
<div id="winner-text"></div>
<script>
const id1 = {{ idLeft }};
const id2 = {{ idRight }};
</script>
<script src="{{ asset('script/combat.js?v=1.4') }}"></script>
</body>
</html>

@ -3,44 +3,77 @@
{% block title %}Accueil - Ma collection de créatures{% endblock %} {% block title %}Accueil - Ma collection de créatures{% endblock %}
{% block stylesheets %} {% block stylesheets %}
<link rel="stylesheet" href="{{ asset('css/home.css') }}"> <link rel="stylesheet" href="{{ asset('css/home.css?v=1.4') }}">
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<h1>🧬 Ma collection de créatures 🐾</h1>
{% if app.user %} {% if app.user %}
<div class="logout-container"> <div class="logout-container">
Connecté en tant que <strong>{{ app.user.username }}</strong> <span style="margin-right: 20px; ">Connecté en tant que <strong>{{ app.user.username }}</strong></span>
<form action="{{ path('app_logout') }}" method="post" style="display: inline;"> <form action="{{ path('app_logout') }}" method="post" style="display: inline;">
<button type="submit" class="btn btn-logout">🚪 Se déconnecter</button> <button type="submit" class="btn btn-logout">🚪Déconnexion</button>
</form> </form>
</div> </div>
<div id="user-data" data-user-id="{{ user.id }}"></div>
{% endif %} {% endif %}
<h1>🧬 Ma collection de créatures 🐾</h1>
<h2 class="section-title">🌱 Vos créatures de base</h2> <h2 class="section-title">🌱 Vos créatures de base</h2>
<div class="emoji-container base"> <div class="emoji-container base">
{% for emoji in emojisDeBase %} {% for emoji in emojisDeBase %}
<div class="emoji-card {{ emoji.color }}" <div class="emoji-card {{ emoji.color }}"
data-id="{{ emoji.id }}" data-id="{{ emoji.id }}"
data-name="{{ emoji.nom }}" data-name="{{ emoji.name }}"
data-color="{{ emoji.color }}" data-color="{{ emoji.color }}"
data-level="{{ emoji.nbCombatGagne }}" data-level="{{ emoji.fightsWon }}"
data-force="{{ emoji.force }}" data-force="{{ emoji.strength }}"
data-vitesse="{{ emoji.vitesse }}"> data-vitesse="{{ emoji.speed }}">
<div class="emoji-level">Level {{ emoji.nbCombatGagne }}</div> <div class="emoji-level">Level {{ emoji.fightsWon }}</div>
<div class="emoji">{{ emoji.code }}</div> <div class="emoji">{{ emoji.code }}</div>
<div class="emoji-name {{ emoji.color }}">{{ emoji.nom }}</div> <div class="emoji-name {{ emoji.color }}">{{ emoji.name }}</div>
<div class="detail-icon" data-id="{{ emoji.id }}"></div> <div class="detail-icon" data-id="{{ emoji.id }}"></div>
<div class="detail-icon-hierarchy" data-id="{{ emoji.id }}">👨‍👩‍👧‍👦</div>
<div class="popup" id="popup-{{ emoji.id }}"> <div class="popup" id="popup-{{ emoji.id }}">
<strong>Stats :</strong><br> <strong>Stats :</strong><br>
Force: {{ emoji.force }}<br> Force: {{ emoji.strength }}<br>
Robustesse: {{ emoji.robustesse }}<br> Robustesse: {{ emoji.toughness }}<br>
Intelligence: {{ emoji.intelligence }}<br> Intelligence: {{ emoji.intelligence }}<br>
Vitesse: {{ emoji.vitesse }} Vitesse: {{ emoji.speed }}
</div>
</div>
{% endfor %}
{% for emoji in emojisDeBase %}
<div class="hierarchy-modal" id="popup-hiera-{{ emoji.id }}">
<div class="hierarchy-overlay" data-id="{{ emoji.id }}"></div>
<div class="hierarchy-content">
<span class="close-btn" data-id="{{ emoji.id }}">&times;</span>
<div class="hierarchy-graph">
<div class="parent-block parent1">
<div class="emoji" id="parent1-{{ emoji.id }}"></div>
<div class="name" id="name-parent1-{{ emoji.id }}"></div>
</div>
<div class="arrows">
<div class="arrow">⬇️</div>
</div>
<div class="parent-block parent2">
<div class="emoji" id="parent2-{{ emoji.id }}"></div>
<div class="name" id="name-parent2-{{ emoji.id }}"></div>
</div>
<div class="child-block">
<div class="child" id="child-{{ emoji.id }}"></div>
<div class="name" id="name-child-{{ emoji.id }}"></div>
</div>
</div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@ -76,23 +109,60 @@
{% for emoji in emojisCrees %} {% for emoji in emojisCrees %}
<div class="emoji-card {{ emoji.color }}" <div class="emoji-card {{ emoji.color }}"
data-id="{{ emoji.id }}" data-id="{{ emoji.id }}"
data-name="{{ emoji.nom }}" data-name="{{ emoji.name }}"
data-color="{{ emoji.color }}" data-color="{{ emoji.color }}"
data-level="{{ emoji.nbCombatGagne }}" data-level="{{ emoji.fightsWon }}"
data-force="{{ emoji.force }}" data-force="{{ emoji.strength }}"
data-vitesse="{{ emoji.vitesse }}"> data-vitesse="{{ emoji.speed }}">
<div class="emoji-level">Level {{ emoji.nbCombatGagne }}</div> <div class="emoji-level">Level {{ emoji.fightsWon }}</div>
<div class="emoji">{{ emoji.code }}</div> <div class="emoji">
<div class="emoji-name {{ emoji.color }}">{{ emoji.nom }}</div> {% if 'vercel' in emoji.code %}
<img src="{{ emoji.code }}" alt="{{ emoji.name }}" width="48" height="48" />
{% else %}
{{ emoji.code }}
{% endif %}
</div>
<div class="emoji-name {{ emoji.color }}">{{ emoji.name }}</div>
<div class="detail-icon" data-id="{{ emoji.id }}"></div> <div class="detail-icon" data-id="{{ emoji.id }}"></div>
<div class="detail-icon-hierarchy" data-id="{{ emoji.id }}">👨‍👩‍👧‍👦</div>
<div class="popup" id="popup-{{ emoji.id }}"> <div class="popup" id="popup-{{ emoji.id }}">
<strong>Stats :</strong><br> <strong>Stats :</strong><br>
Force: {{ emoji.force }}<br> Force: {{ emoji.strength }}<br>
Robustesse: {{ emoji.robustesse }}<br> Robustesse: {{ emoji.toughness }}<br>
Intelligence: {{ emoji.intelligence }}<br> Intelligence: {{ emoji.intelligence }}<br>
Vitesse: {{ emoji.vitesse }} Vitesse: {{ emoji.speed }}
</div>
</div>
{% endfor %}
{% for emoji in emojisCrees %}
<div class="hierarchy-modal" id="popup-hiera-{{ emoji.id }}">
<div class="hierarchy-overlay" data-id="{{ emoji.id }}"></div>
<div class="hierarchy-content">
<span class="close-btn" data-id="{{ emoji.id }}">&times;</span>
<div class="hierarchy-graph">
<div class="parent-block parent1">
<div class="emoji" id="parent1-{{ emoji.id }}"></div>
<div class="name" id="name-parent1-{{ emoji.id }}"></div>
</div>
<div class="arrows">
<div class="arrow">⬇️</div>
</div>
<div class="parent-block parent2">
<div class="emoji" id="parent2-{{ emoji.id }}"></div>
<div class="name" id="name-parent2-{{ emoji.id }}"></div>
</div>
<div class="child-block">
<div class="child" id="child-{{ emoji.id }}"></div>
<div class="name" id="name-child-{{ emoji.id }}"></div>
</div>
</div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@ -111,5 +181,5 @@
{% endblock %} {% endblock %}
{% block javascripts %} {% block javascripts %}
<script src="{{ asset('js/home.js') }}"></script> <script src="{{ asset('js/home.js?v=1.8') }}"></script>
{% endblock %} {% endblock %}

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Emoji Reproduction</title>
<link rel="stylesheet" href="{{ asset('style/reproduction.css?v=1.7') }}" />
</head>
<body>
<div class="field">
<div class="emoji left-emoji"></div>
<div class="emoji right-emoji"></div>
</div>
<div id="child-text"></div>
<div id="heart-gif"></div>
<div>
<button id="btn-retour" class="btn-retour">🏠 Retour à la collection</button>
</div>
<script>
id_user={{ idUser }};
id_left={{ left_emoji }};
id_right={{ right_emoji }};
</script>
<script src="{{ asset('script/reproduction.js?v=1.5') }}"></script>
</body>
</html>

@ -9,16 +9,20 @@ use App\Repository\RarityRepository;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use App\Entity\User;
class EmojiControllerTest extends WebTestCase class EmojiControllerTest extends WebTestCase
{ {
private $client; private $client;
private EntityManagerInterface $em; private EntityManagerInterface $em;
private RarityRepository $rarityRepository;
protected function setUp(): void protected function setUp(): void
{ {
$this->client = static::createClient(); $this->client = static::createClient();
$container = static::getContainer();
$this->em = static::getContainer()->get(EntityManagerInterface::class); $this->em = static::getContainer()->get(EntityManagerInterface::class);
$this->rarityRepository = $container->get(RarityRepository::class);
// Démarre une transaction pour pouvoir annuler les modifications des tests // Démarre une transaction pour pouvoir annuler les modifications des tests
$this->em->getConnection()->beginTransaction(); $this->em->getConnection()->beginTransaction();
@ -33,6 +37,14 @@ class EmojiControllerTest extends WebTestCase
public function testReproduceEmoji(): void public function testReproduceEmoji(): void
{ {
$user = (new User())
->setUsername('testuser')
->setPassword('test');
$this->em->persist($user);
$this->em->flush();
$userId = $user->getId();
$emoji1 = (new Emoji()) $emoji1 = (new Emoji())
->setCode('😀') ->setCode('😀')
->setName('Parent1') ->setName('Parent1')
@ -51,11 +63,11 @@ class EmojiControllerTest extends WebTestCase
->setSpeed(2.0) ->setSpeed(2.0)
->setFightsWon(3); ->setFightsWon(3);
$rarity = (new Rarity()) $rarity = $this->rarityRepository->findOneBy(['name' => 'Common']);
->setName('Rare')
->setDropRate(1.0); $emoji1->setRarity($rarity);
$emoji2->setRarity(rarity: $rarity);
$this->em->persist($rarity);
$this->em->persist($emoji1); $this->em->persist($emoji1);
$this->em->persist($emoji2); $this->em->persist($emoji2);
$this->em->flush(); $this->em->flush();
@ -63,13 +75,11 @@ class EmojiControllerTest extends WebTestCase
$id1 = $emoji1->getId(); $id1 = $emoji1->getId();
$id2 = $emoji2->getId(); $id2 = $emoji2->getId();
$this->client->request('GET', "/emoji/fusion/$id1/$id2"); $this->client->request('GET', "/emojis/fusion/$userId/$id1/$id2");
$response = $this->client->getResponse(); $response = $this->client->getResponse();
$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());
$data = json_decode($response->getContent(), true); $data = json_decode($response->getContent(), true);
$this->assertArrayHasKey('childId', $data);
$this->assertEquals('Child created', $data['message']); $this->assertEquals('Child created', $data['message']);
} }
@ -78,12 +88,12 @@ class EmojiControllerTest extends WebTestCase
$emojiRepo = $this->createMock(EmojiRepository::class); $emojiRepo = $this->createMock(EmojiRepository::class);
$emojiRepo->method('find')->willReturn(null); $emojiRepo->method('find')->willReturn(null);
$this->client->request('GET', '/emoji/fusion/999/998'); $this->client->request('GET', '/emojis/fusion/999/998');
$response = $this->client->getResponse(); $response = $this->client->getResponse();
$this->assertEquals(Response::HTTP_NOT_FOUND, $response->getStatusCode()); $this->assertEquals(Response::HTTP_NOT_FOUND, $response->getStatusCode());
$data = json_decode($response->getContent(), true); $data = json_decode($response->getContent(), true);
$this->assertEquals('One or both emojis not found', $data['error']); $this->assertNull($data);
} }
} }

Loading…
Cancel
Save