Mécaniques de jeu¶
Cette page détaille les mécaniques cœur du gameplay implémentées côté serveur :
la vision (Look), l'inventaire, l'incantation / élévation, le
broadcast et l'éjection. Les algorithmes vivent dans
server/srcs/algorithms/ ; l'orchestration de l'incantation dans
server/srcs/core/IncantationHandler.cpp.
Vision — Look¶
VisionAlgo::getTiles construit le cône de vision du joueur, pour des
profondeurs 0..level, avec 2*depth+1 tuiles par rangée, orientées selon la
Direction (server/srcs/algorithms/VisionAlgo.cpp:6-24) :
for (int depth = 0; depth <= p.level; depth++) {
for (int offset = -depth; offset <= depth; offset++) {
int x = p.x, y = p.y;
if (p.direction == Direction::North) { x = p.x + offset; y = p.y - depth; }
if (p.direction == Direction::South) { x = p.x - offset; y = p.y + depth; }
if (p.direction == Direction::East) { x = p.x + depth; y = p.y + offset; }
if (p.direction == Direction::West) { x = p.x - depth; y = p.y - offset; }
tiles.push_back(&world.getTile(x, y));
}
}
Pourquoi un cône qui s'élargit
La rangée 0 ne contient que la tuile courante. Chaque rangée suivante
avance d'un cran dans la direction du regard et s'élargit de deux tuiles.
Le niveau du joueur fixe la portée : un joueur de niveau N voit
(N+1)² tuiles. world.getTile étant torique, les bords se referment.
formatLook sérialise les tuiles entre crochets, séparées par ,
(VisionAlgo.cpp:56-69). Chaque tuile liste d'abord un jeton player par
joueur présent, puis chaque ressource répétée selon sa quantité, le tout séparé
par des espaces (VisionAlgo.cpp:34-54) :
Exemple : [player, , linemate, food food, ...]. Voir
Protocole IA ↔ Serveur pour la grammaire exacte.
Inventaire¶
CmdInventory::execute renvoie la liste des ressources de l'inventaire du
joueur (server/srcs/commands/CmdInventory.cpp:9-19) :
food multiplié par 126
La nourriture est rapportée × 126 (CmdInventory.cpp:12), pas en unités
brutes. C'est une conversion en ticks restants, incohérente avec l'événement
GUI pin. Voir Limitations connues.
Incantation / élévation¶
L'incantation fait passer un groupe de joueurs au niveau supérieur si les conditions sont réunies sur la tuile.
Table des prérequis¶
IncantationAlgo::TABLE indexe les exigences par level-1
(server/srcs/algorithms/IncantationAlgo.cpp:5-13). Chaque ligne donne le
nombre de joueurs de même niveau requis et le nombre de pierres à
réunir sur la tuile.
| Élévation | Joueurs | linemate | deraumere | sibur | mendiane | phiras | thystame |
|---|---|---|---|---|---|---|---|
| 1 → 2 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
| 2 → 3 | 2 | 1 | 1 | 1 | 0 | 0 | 0 |
| 3 → 4 | 2 | 2 | 0 | 1 | 0 | 2 | 0 |
| 4 → 5 | 4 | 1 | 1 | 2 | 0 | 1 | 0 |
| 5 → 6 | 4 | 1 | 2 | 1 | 3 | 0 | 0 |
| 6 → 7 | 6 | 1 | 2 | 3 | 0 | 1 | 0 |
| 7 → 8 | 6 | 2 | 2 | 2 | 2 | 2 | 1 |
Double validation¶
IncantationAlgo::check vérifie qu'il y a assez de joueurs au niveau exact
sur la tuile, puis que chaque pierre est présente en quantité suffisante
(IncantationAlgo.cpp:15-38). Cette vérification est faite deux fois : au
démarrage et à la fin (re‑validation), pour empêcher la triche pendant les
300 ticks d'attente. execute consomme les pierres et incrémente le niveau de
chaque participant (IncantationAlgo.cpp:40-57).
Déroulé — IncantationHandler¶
Le démarrage est porté par IncantationHandler::start
(server/srcs/core/IncantationHandler.cpp:14-189). CmdIncantation::execute
est, lui, un no‑op : toute la logique est dans le handler.
- Rassembler sur la tuile les joueurs de même niveau et non occupés
(
isBusy() == false) —IncantationHandler.cpp:34-48. check; en cas d'échec, répondrekoet abandonner.- Succès :
isIncanting = truepour tous, l'initiateur passebusy, chaque participant reçoitElevation underway, diffusionpicaux GUIs (IncantationHandler.cpp:57-73). - Planifier la complétion après 300 ticks (
cmd->getDelay()). - À l'échéance : re‑résoudre les joueurs par
id, vérifier qu'aucun n'a bougé, est mort ou a changé de tuile, puis re‑validercheck(IncantationHandler.cpp:126-144). - Succès :
executeconsomme les pierres etlevel++; chaque participant reçoitCurrent level: N; diffusionpie 1,bct,plv(IncantationHandler.cpp:146-173). - Échec à la re‑validation :
isIncanting = false,koà tous, diffusionpie 0(IncantationHandler.cpp:100-124).
flowchart TD
A["Incantation dépilée"] --> B["Rassembler joueurs<br/>même niveau, non busy"]
B --> C{"check() ?"}
C -->|non| K1["ko"]
C -->|oui| D["isIncanting = true<br/>Elevation underway<br/>diffusion pic"]
D --> E["Attente 300 ticks"]
E --> F{"Joueurs intacts<br/>+ re-check() ?"}
F -->|non| K2["ko + pie 0"]
F -->|oui| G["execute : -pierres, level++"]
G --> H["Current level: N<br/>diffusion pie 1 / bct / plv"]
H --> I{"≥ 6 joueurs niveau 8 ?"}
I -->|oui| W["seg + arrêt"]
I -->|non| Z["fin"]
Condition de victoire¶
Après une élévation réussie, si l'équipe compte ≥ 6 joueurs de niveau 8, le
serveur diffuse seg <équipe> et programme l'arrêt
(_stopWhenFlushed = true, IncantationHandler.cpp:161-176).
bool gameWon = countLevel8Players(initiator->teamName) >= 6;
...
if (gameWon) protocol.sendSeg(winningTeam);
...
if (gameWon) stopWhenFlushed = true;
Broadcast¶
Quand un joueur émet Broadcast <texte>, chaque autre joueur reçoit le message
accompagné d'une direction sonore K (0..8) indiquant d'où vient le son
relativement à son orientation (0 = même tuile).
BroadcastAlgo::computeK calcule le vecteur torique le plus court de
l'émetteur vers le récepteur, l'angle atan2(dy, dx), le décale de
l'orientation du récepteur, puis arrondit sur les 8 secteurs
(server/srcs/algorithms/BroadcastAlgo.cpp:14-57) :
if (emitter.x == receiver.x && emitter.y == receiver.y)
return 0; // même tuile
...
double alpha = std::atan2(dy, dx); // vecteur récepteur -> émetteur
// theta selon la direction du récepteur (N=-pi/2, E=0, S=pi/2, W=pi)
double rel_alpha = alpha - theta;
int index = (int)std::round(-rel_alpha / (M_PI / 4));
int k = 1 + index; // ramené dans 1..8
CmdBroadcast::execute parcourt toutes les tuiles et calcule K pour chaque
autre joueur (server/srcs/commands/CmdBroadcast.cpp:15-29). Le serveur envoie
ensuite à chaque récepteur message K, <texte> et diffuse pbc aux GUIs
(server/srcs/core/Server.cpp:344-364).
Repère de K
K = 1 correspond à un son venant de l'avant ; les indices augmentent dans
le sens trigonométrique inverse. K = 0 est réservé au cas « émetteur sur
la même tuile ». La projection torique garantit qu'on entend toujours le
son par le chemin le plus court.
Éjection — Eject¶
Eject pousse tous les autres joueurs présents sur la tuile d'une case dans
la direction du regard de l'éjecteur (déplacement torique), et réussit s'il a
poussé au moins un joueur (server/srcs/commands/CmdEject.cpp:21-53) :
int dx = 0, dy = 0;
if (player.direction == Direction::North) dy = -1;
if (player.direction == Direction::South) dy = 1;
if (player.direction == Direction::East) dx = 1;
if (player.direction == Direction::West) dx = -1;
...
for (Player* p : this->_ejectedPlayers) {
currentTile.removePlayer(p);
p->x = ((p->x + dx) % w + w) % w;
p->y = ((p->y + dy) % h + h) % h;
world.getTile(p->x, p->y).addPlayer(p);
}
_success = !this->_ejectedPlayers.empty();
Côté serveur (server/srcs/core/Server.cpp:268-309), pour chaque victime un
eject: K est envoyé (K calculé par ejectDirectionToK selon la direction de
poussée et celle de la victime), puis tous les œufs de la tuile sont
détruits (edi + delete), et l'on diffuse pex (éjecteur) et ppo
(victimes déplacées).
Œufs détruits par l'éjection
Tout œuf présent sur la tuile au moment de l'éjection est supprimé, ce qui réduit d'autant les slots de connexion de l'équipe concernée. Voir Joueurs, équipes & œufs et Limitations connues.
Voir aussi¶
- Joueurs, équipes & œufs — entités manipulées par ces mécaniques.
- Commandes & délais — coûts en ticks de chaque action.
- Protocole IA ↔ Serveur — formats fil exacts.