Archives mensuelles : juillet 2012

Descente dans les donjons du destin #1

Je démarre cette série d’articles au sujet d’un jeu vidéo. J’avoue être ignorant de la plupart des blockbusters vidéoludiques, comme World of Warcraft ou Mario Bros et n’avoir jamais possédé de console de jeux. Il y a bien un jeu méconnu, mais dont on ne se lasse pas, c’est Nethack.

Ce jeu conserve un certain exotisme et a ses inconditionnels, malgré son grand âge. On y jouait déjà l’époque des petits écrans cathodiques à 16 couleurs, et des disquettes souples, que je n’ai pas vraiment connu, puisque la première version de ce jeu est sortie en 1987, année de ma naissance. Avec les moyens techniques de l’époque, il n’était pas question de graphismes 3D, ni même de temps réel; on dessinait les pixels un à un, ou bien on écrivait des caractères sur l’écran.

Nethack était un de ces jeux, qui utilisait des caractères ASCII, là où les jeux d’aujourd’hui simulent fidèlement le monde réel. Nethack est un jeu de rôle de type porte-monstre-trésor, qui fait partie de la grande famille des Rogue-like. Ces jeux, souvent parents, placent le joueur dans un univers de fantasy, où il explore des souterrains abandonnés, combat des orques et autres fantômes, et récolte des richesses. L’environnement est une grille en deux dimensions formée de caractères, où @ représente le joueur, + un mur ou G un gnome. La représentation graphique est plus métaphorique que figurative, elle tend plus vers le roman que vers le cinéma. En ce temps-là, la souris était encore un animal exotique, et Nethack s’en passe. D’ailleurs le jeu se passe au tour par tour, comme dans les jeux de rôle sur plateau.

Toutes les interactions se passent au clavier, ainsi q permet de boire une potion (quaff en anglais), et on enfile un vêtement avec maj+w. Ceci fait quelques combinaisons de touches à retenir, mais on retient assez vite ces raccourcis,qui sont aussi donnés en mode graphique.

La version la plus basique de Nethack est toujours en mode texte, mais on peut à présent jouer à ce jeu légendaire dans des interfaces plus pratiques et surtout plus lisibles. Il est en effet, plus facile de reconnaître les monstres sur des petites images que se souvenir de la différences entre un o gris clair, simple gobelin pauvrement armé, et un o gris, l’Uruk-Haï qui peut vous tuer immédiatement d’une flèche empoisonnée.

Pourquoi s’intéresser à un jeu aussi archaïque qui ne doit plus être maintenu depuis longtemps ? Parce Nethack est peut-être le jeu le plus addictif, le plus difficile et celui qui offre la plus grande durée de vie.

Nethack offre un défi apparemment simple: récupérer le plus puissant objet objet magique de l’univers, l’amulette de Yendor. Le commanditaire est votre dieu, qui n’a pas d’autres occupations que chercher querelle aux autres dieux pour être adoré de tous, l’amulette lui permettrait d’établir sa suprématie. Pour cela, il faut affronter de nombreux ennemis, traverser des lieux redoutables, et vaincre le magicien de Yendor. Contrairement à la grande majorité des jeux, où l’on peut recommencer plusieurs fois après un échec, le personnage de Nethack n’a qu’une vie; même si après dix heures de jeu, vous mourez bêtement d’indigestion, il n’y a plus qu’à recommencer.

Il existe un wiki à propos de Nethack, qui compte plus de 2000 pages, et ce n’est pas sans raison, car les règles de Nethack sont assez complexes, il existe de nombreux objets différents, aux usages parfois inattendus, des mécanismes de compétences, de multiples caractéristiques qui affectent le personnage, une foultitude de monstres dont même les plus insignifiants peuvent être des pièges mortels.

La difficulté du jeu et la forte mortalité des aventuriers dans les donjons maléfiques font qu’on ne finit pas le jeu en dix heures. Les joueurs qui « ascendent », ceux qui accomplissent la mission, ont en général au moins un an d’expérience du jeu. Jouant à Nethack irrégulièrement depuis trois ans, je n’ai pas encore réussi.

Comment installer Nethack ? Même si à l’origine, Nethack se joue exclusivement avec le clavier et en mode texte, il existe une certain nombre d’interfaces, plus ou moins conviviales et élaborées. Supposant que vous n’êtes pas spécialement masochiste et que vous vous servez de la souris de temps à autres, les interfaces suivantes sont recommandées:

  • Sous GNU/Linux, Qt-Nethack est une interface particulièrement claire et agréable, même si elle date un peu (paquet nethack-qt sous Ubuntu)
  • Sous Windows, Nethack a aussi une version graphique assez utilisable

Parmi d’autres interfaces, on peut citer Falcon’s Eye, qui représente l’environnement en vue isométrique avec les graphismes les plus soignés. La vue isométrique peut convenir à certains, mais désoriente quelque peu.

Le jeu est peu bavard sur lui même, son site officiel ne donne quasiment qu’un lien de téléchargement, et l’aide dans Nethack ne donne que le minimum d’informations. C’est le principe des jeux Rogue-like d’avoir des règles obscures qui ne sont pas en principe dévoilées au joueur. Or comme Nethack est un logiciel libre, toute personne versée un temps soit peu dans l’informatique peut comprendre les règles en lisant le code source. Depuis longtemps, ces gens publient des spoilers, fichiers qui dévoilent les mécanismes internes du jeu comme tous les événements qui peuvent vous arriver en buvant l’eau d’une fontaine, comment identifier les parchemins selon leur prix en magasin et bien d’autres choses qu’on ne saurait pas normalement. Pour certains, consulter des spoilers gâche le plaisir de découvrir le jeu par soi-même, mais ceux ci permettent de progresser plus rapidement et d’apprendre des techniques intéressantes. Les articles de cette série seront en grande partie des spoilers.

Dernier avertissement avant de descendre au coeur du danger: le jeu n’est développé qu’en anglais, jouer à Nethack ne requiert pas un niveau exceptionnel, mais ceci peut rebuter.

La magie du regroupement dans OpenLayers

Quand on affiche tout un tas de points d’intérêts sur une carte, leur répartition n’est pas toujours uniforme, ce qui occasionne parfois un agglutinement des points d’intérêt qui s’empilent dans une mêlée confuse.

Pour gérer proprement des points d’intérêt nombreux et qui ont tendance à se confondre, on dispose dans OpenLayers d’un mécanisme de regroupement, qu’on désigne en anglais sous le nom de clustering. Plutôt que dessiner tous les points tel quel, la bibliothèque va se charger de regrouper les points très rapprochés en un seul point d’intérêt. Ce système, bien que puissant est un peu compliqué, comme bien d’autres choses dans OpenLayers.

Voyons point par point comment mettre en œuvre le regroupement sur une couche de points, gérer l’apparence des points regroupés et prendre en compte les événements sur une telle couche.

Propriétés de la couche

Le regroupement dans OpenLayers est implémenté comme une stratégie, c’est à dire un objet qui change de comportement d’une couche. Voici la définition de notre couche de point d’intérêt, un objet OpenLayers.Strategy.Cluster() est fourni dans la propriété strategies.

var layerPOI = new OpenLayers.Layer.Vector("Points d'intérêt", {
	projection: new OpenLayers.Projection("EPSG:4326"),
	strategies: [
		new OpenLayers.Strategy.Cluster()
	],
	styleMap:new OpenLayers.StyleMap({
           "default": clusterStyle
	})
});

Nota: Nous avons référencé un style nommé clusterStyle, que nous expliciterons plus tard.

Manipuler les groupes

Il est nécessaire ici d’adapter votre code concernant la gestion des événements liés à la couche. Quand on implémente un contrôle, comme SelectFeature pour intercepter le clic sur un objet, sans regroupement d’objets, on reçoit un objet OpenLayers.Vector, dont les propriétés sont:

  • attributes:  tableau des attributs de l’objets
  • geometry: une géométrie

Quand on ajoute à cela le regroupement, on reçoit plus des OpenLayers.Vector, mais un objet avec deux attributs:

  • count: nombre de points d’intérêt dans le groupe
  • cluster: tableau des objets du groupe, ce sont des OpenLayers.Vector

Re-définir le style

Pour la couche, on doit définir un style avec la classe OpenLayers.Style. Comme dans toutes les déclarations de style dans OpenLayers, on y définit les valeurs de différents attributs comme le label, la grosseur d’un point ou l’adresse d’un pictogramme. Mais comme on ne souhaite pas forcément rendre un point d’intérêt seul de la même manière que le regroupement de dix points d’intérêt, on peut calculer des propriétés de style en fonction du groupe de points d’intérêt. Là où on donne souvent des valeurs constantes pour une propriété, on peut déclarer dans les attributs des « variables ». Par exemple, dans la propriété label, on utilise la déclaration ${nombre}, qui désigne une valeur à calculer lors du rendu de la couche. Les variables sont calculées dans des méthodes du même nom, qui font partie de l’objet context.

Dans cet exemple, on affiche comme label le nombre d’éléments d’un groupe d’au moins deux éléments, avec la variable ${nombre}.

var clusterStyle = new OpenLayers.Style({
    label:"${nombre}",
    graphicWidth: 20,
    graphicHeight: 20
}, {
    context: {
         nombre: function(feature) {
             if(feature.attributes.count>=2)
            	 return feature.attributes.count;
             else
            	 return "";
         }
    }
});

Critères de regroupement

L’algorithme de regroupement d’OpenLayers est relativement simple: on regroupe tous les objets qui, à l’écran, sont à moins de 40 pixels de distance.

On peut vouloir personnaliser ce comportement, pour prendre en compte d’autres règles de gestion. Dans ce cas, après avoir instancié la stratégie de regroupement, on doit re-définir sa méthode shouldCluster. Partant d’un groupe existant, nommé cluster et d’un objet feature qu’on pourrait y ajouter, cette méthode décide de la possibilité de fusionner le groupe avec un élément, en retournant un booléen. Dans cet exemple, on a modifié la stratégie pour ne regrouper que les objets qui ont la même valeur pour l’attribut type et qui sont distant d’au plus 35 pixels.

var clusterCat = new OpenLayers.Strategy.Cluster();
clusterCat.shouldCluster = function(cluster,feature)
{
	if(cluster.cluster[0].attributes.type != feature.attributes.type)
	{
		return false;
	}
	var cc = cluster.geometry.getBounds().getCenterLonLat();
        var fc = feature.geometry.getBounds().getCenterLonLat();
        var distance = (Math.sqrt(Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2)) / this.resolution);
        return (distance <= 35);
}