Mis à jour le lundi 8 janvier 2018
  • 30 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

TP : Des personnages spécialisés

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Avez-vous bien tout retenu du dernier chapitre ? C'est ce que l'on va vérifier maintenant ! En effet, l'héritage est un point très important de la POO qu'il est essentiel de maîtriser, c'est pourquoi je souhaite insister dessus à travers ce TP. Ce dernier est en fait une modification du premier (celui qui mettait en scène notre personnage). Nous allons donc ajouter une possibilité supplémentaire au script : le choix du personnage. Cette modification vous permettra de revoir ce que nous avons abordé depuis le dernier TP, à savoir :

  • L'héritage ;

  • la portéeprotected;

  • l'abstraction ;

  • la résolution statique à la volée.

 

Ce que nous allons faire

Cahier des charges

Je veux que nous ayons le choix de créer un certain type de personnage qui aura certains avantages. Il ne doit pas être possible de créer un personnage « normal » (donc il devra être impossible d'instancier la classePersonnage). Comme précédemment, la classePersonnageaura la liste des colonnes de la table en guise d'attributs.

Je vous donne une liste de personnages différents qui pourront être créés. Chaque personnage a un atout différent sous forme d'entier.

  • Un magicien. Il aura une nouvelle fonctionnalité : celle de lancer un sort qui aura pour effet d'endormir un personnage pendant$atout * 6heures (l'attribut$atoutreprésente la dose de magie du personnage).

  • Un guerrier. Lorsqu'un coup lui est porté, il devra avoir la possibilité de parer le coup en fonction de sa protection (son atout).

Ceci n'est qu'une petite liste de départ. Libre à vous de créer d'autres personnages !

Comme vous le voyez, chaque personnage possède un atout. Cet atout devra être augmenté lorsque le personnage est amené à s'en servir (c'est-à-dire lorsque le magicien lance un sort ou que le guerrier subit des dégâts).

Des nouvelles fonctionnalités pour chaque personnage

Étant donné qu'un magicien peut endormir un personnage, il est nécessaire d'implémenter deux nouvelles fonctionnalités :

  • Celle consistant à savoir si un personnage est endormi ou non (nécessaire lorsque ledit personnage voudra en frapper un autre : s'il est endormi, ça ne doit pas être possible).

  • Celle consistant à obtenir la date du réveil du personnage sous la forme « XX heures, YY minutes et ZZ secondes », qui s'affichera dans le cadre d'information du personnage s'il est endormi.

La base de données

La structure de la BDD ne sera pas la même. En effet, chaque personnage aura un attribut en plus, et surtout, il faut savoir de quel personnage il s'agit (magicien ou guerrier). Nous allons donc créer une colonne type et une colonne atout (l'attribut qu'il a en plus). Une colonne timeEndormi devra aussi être créée pour stocker le timestamp auquel le personnage se réveillera s'il a été ensorcelé. Je vous propose donc cette nouvelle structure (j'ai juste ajouté trois nouveaux champs en fin de table) :

CREATE TABLE IF NOT EXISTS `personnages_v2` (
  `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
  `nom` varchar(50) COLLATE utf8_general_ci NOT NULL,
  `degats` tinyint(3) unsigned NOT NULL DEFAULT '0',
  `timeEndormi` int(10) unsigned NOT NULL DEFAULT '0',
  `type` enum('magicien','guerrier') COLLATE utf8_general_ci NOT NULL,
  `atout` tinyint(3) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;

Le coup de pouce du démarrage

Les modifications que je vous demande peuvent vous donner mal à la tête, c'est pourquoi je me propose de vous mettre sur la voie. Procédons classe par classe.

Le personnage

Là où il y a peut-être une difficulté, c'est pour déterminer l'attribut$type. En effet, où doit-on assigner cette valeur ? Qui doit le faire ?

Pour des raisons évidentes de risques de bugs, ce ne sera pas à l'utilisateur d'assigner la valeur « guerrier » ou « magicien » à cet attribut, mais à la classe elle-même. Cependant, il serait redondant dans chaque classe fille de Personnage de faire un<?php $this->type = 'magicien'par exemple, alors on va le faire une bonne fois pour toute dans la classe Personnage.

Comment cela est-il possible ?

Je vais vous dévoiler une petite fonctionnalité de PHP. Dans toutes les méthodes d'une classe, il est possible d'accéder à son nom grâce à ::class. Pour ce qui est à placer avant ce double deux-points, cela dépend de ce que vous voulez obtenir. Généralement, vous aurez le choix entreself et static. Si vous avez bien compris la partie sur la résolution statique à la volée, vous avez peut-être compris là où je voulais en venir. Essayez ce code :

<?php
class Mere
{
    public function __construct()
	{
		echo static::class;
	}
}

class Fille extends Mere
{

}

new Fille;

À l'écran s'affichera « Fille », car il s'agit de la classe que l'on a appelée. Par conséquent, on souhaite afficher le nom de la classe Fille. Si l'on avait mis self::class, on aurait affiché le nom de la classe Mere.

Le magicien

Qui dit nouvelle fonctionnalité dit nouvelle méthode. Votre classeMagiciendevra donc implémenter une nouvelle méthode permettant de lancer un sort. Celle-ci devra vérifier plusieurs points :

  • La cible à ensorceler n'est pas le magicien qui lance le sort.

  • Le magicien possède encore de la magie (l'atout n'est pas à 0).

Le guerrier

Ce qu'on cherche à faire ici est modifier le comportement du personnage lorsqu'il subit des dégâts. Nous allons donc modifier la méthode qui se charge d'ajouter des dégâts au personnage. Cette méthode procédera de la sorte :

  • Elle calculera d'abord la valeur de l'atout.

  • Elle augmentera les dégâts en prenant soin de prendre en compte l'atout.

  • Elle indiquera si le personnage a été frappé ou tué.

Tu nous parles d'un atout depuis tout à l'heure, mais comment est-ce qu'on le détermine ?

L'atout du magicien et du guerrier se déterminent de la même façon :

  • Si les dégâts sont compris entre 0 et 25, alors l'atout sera de 4.

  • Si les dégâts sont compris entre 25 et 50, alors l'atout sera de 3.

  • Si les dégâts sont compris entre 50 et 75, alors l'atout sera de 2.

  • Si les dégâts sont compris entre 75 et 90, alors l'atout sera de 1.

  • Sinon, il sera de 0.

Comment sont-ils exploités ?

Du côté du guerrier, j'utilise une simple formule : la dose de dégâts reçu ne sera pas de 5, mais de5 - $atout. Du côté du magicien, là aussi j'utilise une simple formule : il endort sa victime pendant($this->atout * 6) * 3600secondes.

Voir le résultat que vous devez obtenir
Comme au précédent TP, le résultat comporte toutes les améliorations proposées en fin de chapitre.

Correction

Nous allons maintenant corriger le TP. Les codes seront d'abord précédés d'explications pour bien mettre au clair ce qui était demandé.

Commençons d'abord par notre classePersonnage. Celle-ci devait implémenter deux nouvelles méthodes (sans compter les getters et setters) :estEndormi()etreveil(). Aussi il ne fallait pas oublier de modifier la méthodefrapper()afin de bien vérifier que le personnage qui frappe n'était pas endormi ! Enfin, il fallait assigner la bonne valeur à l'attribut$typedans le constructeur.

<?php
abstract class Personnage
{
  protected $atout,
            $degats,
            $id,
            $nom,
            $timeEndormi,
            $type;
  
  const CEST_MOI = 1; // Constante renvoyée par la méthode `frapper` si on se frappe soit-même.
  const PERSONNAGE_TUE = 2; // Constante renvoyée par la méthode `frapper` si on a tué le personnage en le frappant.
  const PERSONNAGE_FRAPPE = 3; // Constante renvoyée par la méthode `frapper` si on a bien frappé le personnage.
  const PERSONNAGE_ENSORCELE = 4; // Constante renvoyée par la méthode `lancerUnSort` (voir classe Magicien) si on a bien ensorcelé un personnage.
  const PAS_DE_MAGIE = 5; // Constante renvoyée par la méthode `lancerUnSort` (voir classe Magicien) si on veut jeter un sort alors que la magie du magicien est à 0.
  const PERSO_ENDORMI = 6; // Constante renvoyée par la méthode `frapper` si le personnage qui veut frapper est endormi.
  
  public function __construct(array $donnees)
  {
    $this->hydrate($donnees);
    $this->type = strtolower(static::class);
  }
  
  public function estEndormi()
  {
    return $this->timeEndormi > time();
  }
  
  public function frapper(Personnage $perso)
  {
    if ($perso->id == $this->id)
    {
      return self::CEST_MOI;
    }
    
    if ($this->estEndormi())
    {
      return self::PERSO_ENDORMI;
    }
    
    // On indique au personnage qu'il doit recevoir des dégâts.
    // Puis on retourne la valeur renvoyée par la méthode : self::PERSONNAGE_TUE ou self::PERSONNAGE_FRAPPE.
    return $perso->recevoirDegats();
  }
  
  public function hydrate(array $donnees)
  {
    foreach ($donnees as $key => $value)
    {
      $method = 'set'.ucfirst($key);
      
      if (method_exists($this, $method))
      {
        $this->$method($value);
      }
    }
  }
  
  public function nomValide()
  {
    return !empty($this->nom);
  }
  
  public function recevoirDegats()
  {
    $this->degats += 5;
    
    // Si on a 100 de dégâts ou plus, on supprime le personnage de la BDD.
    if ($this->degats >= 100)
    {
      return self::PERSONNAGE_TUE;
    }
    
    // Sinon, on se contente de mettre à jour les dégâts du personnage.
    return self::PERSONNAGE_FRAPPE;
  }
  
  public function reveil()
  {
    $secondes = $this->timeEndormi;
    $secondes -= time();
    
    $heures = floor($secondes / 3600);
    $secondes -= $heures * 3600;
    $minutes = floor($secondes / 60);
    $secondes -= $minutes * 60;
    
    $heures .= $heures <= 1 ? ' heure' : ' heures';
    $minutes .= $minutes <= 1 ? ' minute' : ' minutes';
    $secondes .= $secondes <= 1 ? ' seconde' : ' secondes';
    
    return $heures . ', ' . $minutes . ' et ' . $secondes;
  }
  
  public function atout()
  {
    return $this->atout;
  }
  
  public function degats()
  {
    return $this->degats;
  }
  
  public function id()
  {
    return $this->id;
  }
  
  public function nom()
  {
    return $this->nom;
  }
  
  public function timeEndormi()
  {
    return $this->timeEndormi;
  }
  
  public function type()
  {
    return $this->type;
  }
  
  public function setAtout($atout)
  {
    $atout = (int) $atout;
    
    if ($atout >= 0 && $atout <= 100)
    {
      $this->atout = $atout;
    }
  }
  
  public function setDegats($degats)
  {
    $degats = (int) $degats;
    
    if ($degats >= 0 && $degats <= 100)
    {
      $this->degats = $degats;
    }
  }
  
  public function setId($id)
  {
    $id = (int) $id;
    
    if ($id > 0)
    {
      $this->id = $id;
    }
  }
  
  public function setNom($nom)
  {
    if (is_string($nom))
    {
      $this->nom = $nom;
    }
  }
  
  public function setTimeEndormi($time)
  {
    $this->timeEndormi = (int) $time;
  }
}

Penchons-nous maintenant vers la classeGuerrier. Celle-ci devait modifier la méthoderecevoirDegats()afin d'ajouter une parade lors d'une attaque.

<?php
class Guerrier extends Personnage
{
  public function recevoirDegats()
  {
    if ($this->degats >= 0 && $this->degats <= 25)
    {
      $this->atout = 4;
    }
    elseif ($this->degats > 25 && $this->degats <= 50)
    {
      $this->atout = 3;
    }
    elseif ($this->degats > 50 && $this->degats <= 75)
    {
      $this->atout = 2;
    }
    elseif ($this->degats > 75 && $this->degats <= 90)
    {
      $this->atout = 1;
    }
    else
    {
      $this->atout = 0;
    }
    
    $this->degats += 5 - $this->atout;
    
    // Si on a 100 de dégâts ou plus, on supprime le personnage de la BDD.
    if ($this->degats >= 100)
    {
      return self::PERSONNAGE_TUE;
    }
    
    // Sinon, on se contente de mettre à jour les dégâts du personnage.
    return self::PERSONNAGE_FRAPPE;
  }
}

Enfin, il faut maintenant s'occuper du magicien. La classe le représentant devait ajouter une nouvelle fonctionnalité : celle de pouvoir lancer un sort.

<?php
class Magicien extends Personnage
{
  public function lancerUnSort(Personnage $perso)
  {
    if ($this->degats >= 0 && $this->degats <= 25)
    {
      $this->atout = 4;
    }
    elseif ($this->degats > 25 && $this->degats <= 50)
    {
      $this->atout = 3;
    }
    elseif ($this->degats > 50 && $this->degats <= 75)
    {
      $this->atout = 2;
    }
    elseif ($this->degats > 75 && $this->degats <= 90)
    {
      $this->atout = 1;
    }
    else
    {
      $this->atout = 0;
    }
    
    if ($perso->id == $this->id)
    {
      return self::CEST_MOI;
    }
    
    if ($this->atout == 0)
    {
      return self::PAS_DE_MAGIE;
    }
    
    if ($this->estEndormi())
    {
      return self::PERSO_ENDORMI;
    }
    
    $perso->timeEndormi = time() + ($this->atout * 6) * 3600;
    
    return self::PERSONNAGE_ENSORCELE;
  }
}

Passons maintenant au manager. Nous allons toujours garder un seul manager parce que nous gérons toujours des personnages ayant la même structure. Les modifications sont mineures : il faut juste ajouter les deux nouveaux champs dans les requêtes et instancier la bonne classe lorsqu'on récupère un personnage.

<?php
class PersonnagesManager
{
  private $db; // Instance de PDO
  
  public function __construct($db)
  {
    $this->db = $db;
  }
  
  public function add(Personnage $perso)
  {
    $q = $this->db->prepare('INSERT INTO personnages_v2(nom, type) VALUES(:nom, :type)');
    
    $q->bindValue(':nom', $perso->nom());
    $q->bindValue(':type', $perso->type());
    
    $q->execute();
    
    $perso->hydrate([
      'id' => $this->db->lastInsertId(),
      'degats' => 0,
      'atout' => 0
    ]);
  }
  
  public function count()
  {
    return $this->db->query('SELECT COUNT(*) FROM personnages_v2')->fetchColumn();
  }
  
  public function delete(Personnage $perso)
  {
    $this->db->exec('DELETE FROM personnages_v2 WHERE id = '.$perso->id());
  }
  
  public function exists($info)
  {
    if (is_int($info)) // On veut voir si tel personnage ayant pour id $info existe.
    {
      return (bool) $this->db->query('SELECT COUNT(*) FROM personnages_v2 WHERE id = '.$info)->fetchColumn();
    }
    
    // Sinon, c'est qu'on veut vérifier que le nom existe ou pas.
    
    $q = $this->db->prepare('SELECT COUNT(*) FROM personnages_v2 WHERE nom = :nom');
    $q->execute([':nom' => $info]);
    
    return (bool) $q->fetchColumn();
  }
  
  public function get($info)
  {
    if (is_int($info))
    {
      $q = $this->db->query('SELECT id, nom, degats, timeEndormi, type, atout FROM personnages_v2 WHERE id = '.$info);
      $perso = $q->fetch(PDO::FETCH_ASSOC);
    }
    
    else
    {
      $q = $this->db->prepare('SELECT id, nom, degats, timeEndormi, type, atout FROM personnages_v2 WHERE nom = :nom');
      $q->execute([':nom' => $info]);
      
      $perso = $q->fetch(PDO::FETCH_ASSOC);
    }
    
    switch ($perso['type'])
    {
      case 'guerrier': return new Guerrier($perso);
      case 'magicien': return new Magicien($perso);
      default: return null;
    }
  }
  
  public function getList($nom)
  {
    $persos = [];
    
    $q = $this->db->prepare('SELECT id, nom, degats, timeEndormi, type, atout FROM personnages_v2 WHERE nom <> :nom ORDER BY nom');
    $q->execute([':nom' => $nom]);
    
    while ($donnees = $q->fetch(PDO::FETCH_ASSOC))
    {
      switch ($donnees['type'])
      {
        case 'guerrier': $persos[] = new Guerrier($donnees); break;
        case 'magicien': $persos[] = new Magicien($donnees); break;
      }
    }
    
    return $persos;
  }
  
  public function update(Personnage $perso)
  {
    $q = $this->db->prepare('UPDATE personnages_v2 SET degats = :degats, timeEndormi = :timeEndormi, atout = :atout WHERE id = :id');
    
    $q->bindValue(':degats', $perso->degats(), PDO::PARAM_INT);
    $q->bindValue(':timeEndormi', $perso->timeEndormi(), PDO::PARAM_INT);
    $q->bindValue(':atout', $perso->atout(), PDO::PARAM_INT);
    $q->bindValue(':id', $perso->id(), PDO::PARAM_INT);
    
    $q->execute();
  }
}

Enfin, finissons par la page d'index qui a légèrement changé. Quelles sont les modifications ?

  • Le formulaire doit proposer au joueur de choisir le type du personnage qu'il veut créer.

  • Lorsqu'on crée un personnage, le script doit créer une instance de la classe désirée et non dePersonnage.

  • Lorsqu'on veut frapper un personnage, on vérifie que l'attaquant n'est pas endormi.

  • Un lien permettant d'ensorceler un personnage doit être ajouté pour les magiciens ainsi que l'antidote qui va avec.

<?php
function chargerClasse($classe)
{
  require $classe . '.php';
}

spl_autoload_register('chargerClasse');

session_start();

if (isset($_GET['deconnexion']))
{
  session_destroy();
  header('Location: .');
  exit();
}

$db = new PDO('mysql:host=localhost;dbname=combats', 'root', '');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);

$manager = new PersonnagesManager($db);

if (isset($_SESSION['perso'])) // Si la session perso existe, on restaure l'objet.
{
  $perso = $_SESSION['perso'];
}

if (isset($_POST['creer']) && isset($_POST['nom'])) // Si on a voulu créer un personnage.
{
  switch ($_POST['type'])
  {
    case 'magicien' :
      $perso = new Magicien(['nom' => $_POST['nom']]);
      break;
    
    case 'guerrier' :
      $perso = new Guerrier(['nom' => $_POST['nom']]);
      break;
    
    default :
      $message = 'Le type du personnage est invalide.';
      break;
  }
  
  if (isset($perso)) // Si le type du personnage est valide, on a créé un personnage.
  {
    if (!$perso->nomValide())
    {
      $message = 'Le nom choisi est invalide.';
      unset($perso);
    }
    elseif ($manager->exists($perso->nom()))
    {
      $message = 'Le nom du personnage est déjà pris.';
      unset($perso);
    }
    else
    {
      $manager->add($perso);
    }
  }
}

elseif (isset($_POST['utiliser']) && isset($_POST['nom'])) // Si on a voulu utiliser un personnage.
{
  if ($manager->exists($_POST['nom'])) // Si celui-ci existe.
  {
    $perso = $manager->get($_POST['nom']);
  }
  else
  {
    $message = 'Ce personnage n\'existe pas !'; // S'il n'existe pas, on affichera ce message.
  }
}

elseif (isset($_GET['frapper'])) // Si on a cliqué sur un personnage pour le frapper.
{
  if (!isset($perso))
  {
    $message = 'Merci de créer un personnage ou de vous identifier.';
  }
  
  else
  {
    if (!$manager->exists((int) $_GET['frapper']))
    {
      $message = 'Le personnage que vous voulez frapper n\'existe pas !';
    }
    
    else
    {
      $persoAFrapper = $manager->get((int) $_GET['frapper']);
      $retour = $perso->frapper($persoAFrapper); // On stocke dans $retour les éventuelles erreurs ou messages que renvoie la méthode frapper.
      
      switch ($retour)
      {
        case Personnage::CEST_MOI :
          $message = 'Mais... pourquoi voulez-vous vous frapper ???';
          break;
        
        case Personnage::PERSONNAGE_FRAPPE :
          $message = 'Le personnage a bien été frappé !';
          
          $manager->update($perso);
          $manager->update($persoAFrapper);
          
          break;
        
        case Personnage::PERSONNAGE_TUE :
          $message = 'Vous avez tué ce personnage !';
          
          $manager->update($perso);
          $manager->delete($persoAFrapper);
          
          break;
        
        case Personnage::PERSO_ENDORMI :
          $message = 'Vous êtes endormi, vous ne pouvez pas frapper de personnage !';
          break;
      }
    }
  }
}

elseif (isset($_GET['ensorceler']))
{
  if (!isset($perso))
  {
    $message = 'Merci de créer un personnage ou de vous identifier.';
  }
  
  else
  {
    // Il faut bien vérifier que le personnage est un magicien.
    if ($perso->type() != 'magicien')
    {
      $message = 'Seuls les magiciens peuvent ensorceler des personnages !';
    }
    
    else
    {
      if (!$manager->exists((int) $_GET['ensorceler']))
      {
        $message = 'Le personnage que vous voulez frapper n\'existe pas !';
      }
      
      else
      {
        $persoAEnsorceler = $manager->get((int) $_GET['ensorceler']);
        $retour = $perso->lancerUnSort($persoAEnsorceler);
        
        switch ($retour)
        {
          case Personnage::CEST_MOI :
            $message = 'Mais... pourquoi voulez-vous vous ensorceler ???';
            break;
          
          case Personnage::PERSONNAGE_ENSORCELE :
            $message = 'Le personnage a bien été ensorcelé !';
            
            $manager->update($perso);
            $manager->update($persoAEnsorceler);
            
            break;
          
          case Personnage::PAS_DE_MAGIE :
            $message = 'Vous n\'avez pas de magie !';
            break;
          
          case Personnage::PERSO_ENDORMI :
            $message = 'Vous êtes endormi, vous ne pouvez pas lancer de sort !';
            break;
        }
      }
    }
  }
}
?>
<!DOCTYPE html>
<html>
  <head>
    <title>TP : Mini jeu de combat - Version 2</title>
    
    <meta charset="utf-8" />
  </head>
  <body>
    <p>Nombre de personnages créés : <?= $manager->count() ?></p>
<?php
if (isset($message)) // On a un message à afficher ?
{
  echo '<p>', $message, '</p>'; // Si oui, on l'affiche
}

if (isset($perso)) // Si on utilise un personnage (nouveau ou pas).
{
?>
    <p><a href="?deconnexion=1">Déconnexion</a></p>
    
    <fieldset>
      <legend>Mes informations</legend>
      <p>
        Type : <?= ucfirst($perso->type()) ?><br />
        Nom : <?= htmlspecialchars($perso->nom()) ?><br />
        Dégâts : <?= $perso->degats() ?><br />
<?php
// On affiche l'atout du personnage suivant son type.
switch ($perso->type())
{
  case 'magicien' :
    echo 'Magie : ';
    break;
  
  case 'guerrier' :
    echo 'Protection : ';
    break;
}

echo $perso->atout();
?>
      </p>
    </fieldset>
    
    <fieldset>
      <legend>Qui attaquer ?</legend>
      <p>
<?php
// On récupère tous les personnages par ordre alphabétique, dont le nom est différent de celui de notre personnage (on va pas se frapper nous-même :p).
$retourPersos = $manager->getList($perso->nom());

if (empty($retourPersos))
{
  echo 'Personne à frapper !';
}

else
{
  if ($perso->estEndormi())
  {
    echo 'Un magicien vous a endormi ! Vous allez vous réveiller dans ', $perso->reveil(), '.';
  }
  
  else
  {
    foreach ($retourPersos as $unPerso)
    {
      echo '<a href="?frapper=', $unPerso->id(), '">', htmlspecialchars($unPerso->nom()), '</a> (dégâts : ', $unPerso->degats(), ' | type : ', $unPerso->type(), ')';
      
      // On ajoute un lien pour lancer un sort si le personnage est un magicien.
      if ($perso->type() == 'magicien')
      {
        echo ' | <a href="?ensorceler=', $unPerso->id(), '">Lancer un sort</a>';
      }
      
      echo '<br />';
    }
  }
}
?>
      </p>
    </fieldset>
<?php
}
else
{
?>
    <form action="" method="post">
      <p>
        Nom : <input type="text" name="nom" maxlength="50" /> <input type="submit" value="Utiliser ce personnage" name="utiliser" /><br />
        Type :
        <select name="type">
          <option value="magicien">Magicien</option>
          <option value="guerrier">Guerrier</option>
        </select>
        <input type="submit" value="Créer ce personnage" name="creer" />
      </p>
    </form>
<?php
}
?>
  </body>
</html>
<?php
if (isset($perso)) // Si on a créé un personnage, on le stocke dans une variable session afin d'économiser une requête SQL.
{
  $_SESSION['perso'] = $perso;
}

Alors, vous commencez à comprendre toute la puissance de la POO et de l'héritage ? Avec une telle structure, vous pourrez à tout moment décider de créer un nouveau personnage très simplement ! Il vous suffit de créer une nouvelle classe, d'ajouter le type du personnage à l'énumération « type » en BDD et de modifier un petit peu le fichier index.php et votre personnage voit le jour ! :)

Améliorations possibles

Comme au précédent TP, beaucoup d'améliorations sont possibles, à commencer par celles déjà exposées dans le chapitre dudit TP :

  • Un système de niveau. Vous pourriez très bien assigner à chaque personnage un niveau de 1 à 100. Le personnage bénéficierait aussi d'une expérience allant de 0 à 100. Lorsque l'expérience atteint 100, le personnage passe au niveau suivant.

  • Un système de force. La force du personnage pourrait augmenter en fonction de son niveau, les dégâts infligés à la victime seront donc plus importants. :pirate:

  • Un système de limitation. En effet, un personnage peut en frapper autant qu'il veut dans un laps de temps indéfini. Pourquoi ne pas le limiter à 3 coups par jour ?

  • Un système de retrait de dégâts. Chaque jour, si l'utilisateur se connecte, il pourrait voir ses dégâts se soustraire de 10 par exemple.

Pour reprendre cet esprit, vous pouvez vous entraîner à créer d'autres personnages, comme une brute par exemple. Son atout dépendrait aussi de ses dégâts et viendrait augmenter sa force lors d'une attaque.

La seule différence avec le premier TP est l'apparition de nouveaux personnages, les seules améliorations différentes possibles sont donc justement la création de nouveaux personnages. Ainsi je vous encourage à imaginer tout un tas de différents personnages tous aussi farfelus les uns que les autres : cela vous fera grandement progresser et vous aidera à vous familiariser avec l'héritage !

Exemple de certificat de réussite
Exemple de certificat de réussite