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

raph_combat
Raphael LACOTE 2 days ago
commit 245f301602

@ -11,12 +11,45 @@ h1 {
color: #f8b435; color: #f8b435;
} }
.emoji-container { .section-divider {
border: none;
border-top: 3px solid #f8b435;
margin: 50px auto 20px;
width: 80%;
}
.section-break-icon {
text-align: center;
font-size: 2rem;
margin: -20px 0;
color: #f8b435;
}
.section-title {
margin: 40px 10px;
font-size: 1.8rem;
color: #f8f5e0;
}
.emoji-container:not(.base) {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 30px;
width: fit-content;
margin: 0 auto;
}
.emoji-container.base {
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: 30px; gap: 30px;
margin-bottom: 10rem;
border: 3px dashed #f8b435;
padding: 20px;
margin: 0 auto 30px auto;
border-radius: 15px;
width: fit-content;
} }
.emoji-card { .emoji-card {
@ -43,7 +76,7 @@ h1 {
/* Animation brillance pour rareté légendaire */ /* Animation brillance pour rareté légendaire */
@keyframes shine { @keyframes shine {
0% { background-position: 0px; } 0% { background-position: 0px; }
100% { background-position: 177px; } 100% { background-position: 168px; }
} }
.emoji-card.gold { .emoji-card.gold {
@ -54,17 +87,17 @@ h1 {
.emoji-card.gold::before { .emoji-card.gold::before {
content: ''; content: '';
position: absolute; position: absolute;
top: 0; top: 1%;
left: 1%; left: 1%;
width: 98%; width: 97%;
height: 100%; height: 99%;
background: linear-gradient( background: linear-gradient(
120deg, 120deg,
rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.5) 50%, rgba(255, 255, 255, 0.8) 50%,
rgba(255, 255, 255, 0) 100% rgba(255, 255, 255, 0) 100%
); );
animation: shine 5s infinite; animation: shine 3.5s infinite;
pointer-events: none; pointer-events: none;
z-index: -1; z-index: -1;
} }
@ -219,7 +252,7 @@ color: #555;
#selection-status { #selection-status {
font-size: 1.1rem; font-size: 1.1rem;
margin-bottom: 20px; margin-top: 4rem;
font-weight: bold; font-weight: bold;
color: #f9e8c0; color: #f9e8c0;
} }
@ -246,3 +279,36 @@ color: #555;
font-size: 1.5rem; font-size: 1.5rem;
font-weight: bold; font-weight: bold;
} }
/* Pagination */
.pagination-bar {
margin: 4rem 4rem;
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
color: #f8f5e0;
font-family: 'Georgia', serif;
}
.pagination-bar button {
background: none;
border: none;
font-size: 1.1rem;
color: #f8f5e0;
cursor: pointer;
padding: 5px 10px;
border-radius: 50%;
transition: background 0.2s ease;
}
.pagination-bar button.active {
background: #333;
color: white;
font-weight: bold;
}
.pagination-bar button:hover:not(.active) {
color: #f8b435;
}

@ -77,7 +77,7 @@ document.addEventListener('DOMContentLoaded', () => {
console.log(`Accouplement : ${name1} et ${name2}`); console.log(`Accouplement : ${name1} et ${name2}`);
} }
// Réinitialiser après l'action // Réinitialiser après l'action (seulement si pas redirigé)
selectedCards.forEach(card => card.classList.remove('selected')); selectedCards.forEach(card => card.classList.remove('selected'));
selectedCards = []; selectedCards = [];
updateSelectionDisplay(); updateSelectionDisplay();
@ -86,8 +86,10 @@ document.addEventListener('DOMContentLoaded', () => {
// Ouvre / Ferme la popup d'information // Ouvre / Ferme la popup d'information
function togglePopup(id) { function togglePopup(id) {
const popup = document.getElementById('popup-' + id); const popup = document.getElementById('popup-' + id);
if (popup) {
popup.style.display = (popup.style.display === 'block') ? 'none' : 'block'; popup.style.display = (popup.style.display === 'block') ? 'none' : 'block';
} }
}
// Fermer les autres popups en cliquant ailleurs // Fermer les autres popups en cliquant ailleurs
document.addEventListener('click', function(e) { document.addEventListener('click', function(e) {
@ -104,7 +106,7 @@ document.addEventListener('DOMContentLoaded', () => {
const sortBy = document.getElementById('sort-select').value; const sortBy = document.getElementById('sort-select').value;
const searchTerm = document.getElementById('search-input').value.toLowerCase(); const searchTerm = document.getElementById('search-input').value.toLowerCase();
const cards = Array.from(document.querySelectorAll('.emoji-card')); const cards = Array.from(document.querySelectorAll('#collection-container .emoji-card'));
cards.forEach(card => { cards.forEach(card => {
const color = card.dataset.color; const color = card.dataset.color;
@ -121,7 +123,7 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
if (sortBy !== 'none') { if (sortBy !== 'none') {
const container = document.querySelector('.emoji-container'); const container = document.getElementById('collection-container'); // ✅ corrigé
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) => {
@ -131,21 +133,29 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
visibleCards.forEach(card => container.appendChild(card)); visibleCards.forEach(card => container.appendChild(card));
paginateCards();
} }
} }
// Appel Fonctionnalité de popup d'information // Appel Fonctionnalité de popup d'information
document.querySelectorAll('.detail-icon').forEach(icon => { document.querySelectorAll('.detail-icon').forEach(icon => {
icon.addEventListener('click', (e) => { icon.addEventListener('click', (e) => {
const id = icon.parentElement.dataset.id; const card = icon.closest('.emoji-card');
const id = card?.dataset.id;
if (id) {
togglePopup(id); togglePopup(id);
e.stopPropagation(); // empêche le clic daller à la carte }
e.stopPropagation(); // évite que ça sélectionne la carte
}); });
}); });
// Appel Fonctionnalité de sélection des cartes // Appliquer l'écouteur à toutes les cartes (base + collection)
document.querySelectorAll('.emoji-card').forEach(card => { document.querySelectorAll('.emoji-container').forEach(container => {
card.addEventListener('click', () => toggleSelection(card)); container.addEventListener('click', (e) => {
const card = e.target.closest('.emoji-card');
if (!card || e.target.closest('.detail-icon')) return; // Ignore si clic sur icône
toggleSelection(card);
});
}); });
// Appel Fonctionnalité de combat et reproduction // Appel Fonctionnalité de combat et reproduction
@ -160,4 +170,66 @@ document.addEventListener('DOMContentLoaded', () => {
document.getElementById('search-input').addEventListener('input', applyFilters); document.getElementById('search-input').addEventListener('input', applyFilters);
document.getElementById('rarete-filter').addEventListener('change', applyFilters); document.getElementById('rarete-filter').addEventListener('change', applyFilters);
document.getElementById('sort-select').addEventListener('change', applyFilters); document.getElementById('sort-select').addEventListener('change', applyFilters);
/***** Partie Pagination ******/
const ITEMS_PER_PAGE = 10;
let currentPage = 1;
function paginateCards() {
const allCards = Array.from(document.querySelectorAll('#collection-container .emoji-card'));
const container = document.getElementById('collection-container');
const totalPages = Math.ceil(allCards.length / ITEMS_PER_PAGE);
// Masquer toutes les cartes
allCards.forEach(card => card.style.display = 'none');
// Afficher uniquement les cartes de la page courante
const start = (currentPage - 1) * ITEMS_PER_PAGE;
const end = start + ITEMS_PER_PAGE;
allCards.slice(start, end).forEach(card => card.style.display = 'block');
renderPagination(totalPages);
}
function renderPagination(totalPages) {
const pagination = document.getElementById('pagination');
pagination.innerHTML = '';
const addBtn = (text, page = null, isActive = false, disabled = false) => {
const btn = document.createElement('button');
btn.textContent = text;
if (isActive) btn.classList.add('active');
if (disabled) btn.disabled = true;
if (page !== null) {
btn.addEventListener('click', () => {
currentPage = page;
paginateCards();
});
}
pagination.appendChild(btn);
};
addBtn('Précédent', currentPage - 1, false, currentPage === 1);
for (let i = 1; i <= totalPages; i++) {
if (
i === 1 ||
i === totalPages ||
(i >= currentPage - 1 && i <= currentPage + 1)
) {
addBtn(i, i, i === currentPage);
} else if (
i === 2 && currentPage > 3 ||
i === totalPages - 1 && currentPage < totalPages - 2
) {
addBtn('...');
}
}
addBtn('Suivant', currentPage + 1, false, currentPage === totalPages);
}
paginateCards();
}); });

@ -0,0 +1,76 @@
<?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 PopulateDBEmojiAvailableCommand extends Command
{
protected static $defaultName = 'app:popDBEmojiAvai';
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 stock_emoji');
// On crée la table
$this->connection->executeStatement('
CREATE TABLE stock_emoji (
id INT PRIMARY KEY,
code VARCHAR(255) NOT NULL
)
');
// On peuple la table
$this->connection->executeStatement("INSERT INTO stock_emoji (id, code) VALUES
(1,'🤖'),
(2,'😺'),
(3,'🧠'),
(4,'👻'),
(5,'🧟'),
(6,'🐶'),
(7,'👽'),
(8,'🧛'),
(9,'🎃'),
(10,'🐸'),
(11,'⚡'),
(12,'💀'),
(13,'🔥'),
(14,'🧙'),
(15,'🌪️'),
(16,'😎'),
(17,'😁'),
(18,'🌟'),
(19,'😈')
");
$output->writeln('Base de données peuplée.');
} catch (\Exception $e) {
$output->writeln('<error>Erreur : ' . $e->getMessage() . '</error>');
return Command::FAILURE;
}
return Command::SUCCESS;
}
}

@ -11,7 +11,7 @@ class HomeController extends AbstractController
#[Route('/', name: 'home')] #[Route('/', name: 'home')]
public function index(): Response public function index(): Response
{ {
$emojis = [ $emojisDeBase = [
[ [
'id' => 1, 'id' => 1,
'nom' => 'Bob', 'nom' => 'Bob',
@ -21,7 +21,7 @@ class HomeController extends AbstractController
'intelligence' => 7.8, 'intelligence' => 7.8,
'vitesse' => 10.0, 'vitesse' => 10.0,
'nbCombatGagne' => 3, 'nbCombatGagne' => 3,
'rarete' => 2, // épique 'rarete' => 3, // épique
], ],
[ [
'id' => 2, 'id' => 2,
@ -43,24 +43,169 @@ class HomeController extends AbstractController
'intelligence' => 18.0, 'intelligence' => 18.0,
'vitesse' => 17.0, 'vitesse' => 17.0,
'nbCombatGagne' => 10, 'nbCombatGagne' => 10,
'rarete' => 4, // légendaire '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
] ]
]; ];
// Ajout de la couleur selon la rareté $emojisCrees = [
foreach ($emojis as &$emoji) { [
'id' => 6,
'nom' => 'Benoit',
'code' => '🤖',
'force' => 15.0,
'robustesse' => 12.0,
'intelligence' => 14.0,
'vitesse' => 13.0,
'nbCombatGagne' => 5,
'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) {
$emoji['color'] = match ($emoji['rarete']) {
2 => 'green',
3 => 'purple',
4 => 'red',
5 => 'gold',
default => 'gray',
};
}
foreach ($emojisCrees as &$emoji) {
$emoji['color'] = match ($emoji['rarete']) { $emoji['color'] = match ($emoji['rarete']) {
1 => 'green', // commun 2 => 'green',
2 => 'purple', // épique 3 => 'purple',
3 => 'red', // mythique 4 => 'red',
4 => 'gold', // légendaire 5 => 'gold',
default => 'gray' default => 'gray',
}; };
} }
return $this->render('home/index.html.twig', [ return $this->render('home/index.html.twig', [
'emojis' => $emojis, 'emojisDeBase' => $emojisDeBase,
'emojisCrees' => $emojisCrees,
]); ]);
} }
} }

@ -0,0 +1,35 @@
<?php
namespace App\Entity;
use App\Repository\StockEmojiRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: StockEmojiRepository::class)]
class StockEmoji
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $code = null;
public function getId(): ?int
{
return $this->id;
}
public function getCode(): ?string
{
return $this->code;
}
public function setCode(string $code): self
{
$this->code = $code;
return $this;
}
}

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

@ -10,6 +10,38 @@
<h1>🧬 Ma collection de créatures 🐾</h1> <h1>🧬 Ma collection de créatures 🐾</h1>
<h2 class="section-title">🌱 Vos créatures de base</h2>
<div class="emoji-container base">
{% for emoji in emojisDeBase %}
<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" data-id="{{ emoji.id }}"></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 class="section-break-icon">🔻</div>
<h2 class="section-title">📦 Votre collection personnelle</h2>
<div class="filter-bar"> <div class="filter-bar">
<label for="search-input">🔍 Rechercher un nom :</label> <label for="search-input">🔍 Rechercher un nom :</label>
<input type="text" id="search-input" placeholder="ex: Bob..." /> <input type="text" id="search-input" placeholder="ex: Bob..." />
@ -32,18 +64,20 @@
</select> </select>
</div> </div>
<div id="selection-status">Sélectionnez 2 créatures...</div> <div class="emoji-container" id="collection-container">
{% for emoji in emojisCrees %}
<div class="emoji-container"> <div class="emoji-card {{ emoji.color }}"
{% for emoji in emojis %} data-id="{{ emoji.id }}"
<div class="emoji-card {{ emoji.color }}" data-id="{{ emoji.id }}" data-name="{{ emoji.nom }}" data-name="{{ emoji.nom }}"
data-color="{{ emoji.color }}" data-level="{{ emoji.nbCombatGagne }}" data-color="{{ emoji.color }}"
data-force="{{ emoji.force }}" data-vitesse="{{ emoji.vitesse }}"> data-level="{{ emoji.nbCombatGagne }}"
data-force="{{ emoji.force }}"
data-vitesse="{{ emoji.vitesse }}">
<div class="emoji-level">Level {{ emoji.nbCombatGagne }}</div> <div class="emoji-level">Level {{ emoji.nbCombatGagne }}</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.nom }}</div>
<div class="detail-icon"></div> <div class="detail-icon" 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>
@ -56,11 +90,15 @@
{% endfor %} {% endfor %}
</div> </div>
<div id="pagination" class="pagination-bar"></div>
<div id="selection-status">Sélectionnez 2 créatures de votre choix pour commencer ...</div>
<div id="selection-visual" class="selection-visual"></div> <div id="selection-visual" class="selection-visual"></div>
<div class="action-buttons"> <div class="action-buttons">
<button class="btn" ">⚔️ Combattre</button> <button class="btn">⚔️ Combattre</button>
<button class="btn" ">💞 Reproduire</button> <button class="btn">💞 Reproduire</button>
</div> </div>
{% endblock %} {% endblock %}

Loading…
Cancel
Save