Aller au contenu

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) :

[joueur courant, tuile1, tuile2, ...]

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 N, linemate N, deraumere N, sibur N, mendiane N, phiras N, thystame N]

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.

  1. Rassembler sur la tuile les joueurs de même niveau et non occupés (isBusy() == false) — IncantationHandler.cpp:34-48.
  2. check ; en cas d'échec, répondre ko et abandonner.
  3. Succès : isIncanting = true pour tous, l'initiateur passe busy, chaque participant reçoit Elevation underway, diffusion pic aux GUIs (IncantationHandler.cpp:57-73).
  4. Planifier la complétion après 300 ticks (cmd->getDelay()).
  5. À 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‑valider check (IncantationHandler.cpp:126-144).
  6. Succès : execute consomme les pierres et level++ ; chaque participant reçoit Current level: N ; diffusion pie 1, bct, plv (IncantationHandler.cpp:146-173).
  7. Échec à la re‑validation : isIncanting = false, ko à tous, diffusion pie 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