Celà fait longtemps que je veux réaliser un jeu du royaume, c'est à dire un jeu où l'on gère un domaine quelconque (oui, c'est le nom originel de ce genre de jeux). Eh bon, après environ 25 ans, me voilà à l'ouvrage.

samedi 16 juin 2012

Mise à jour d'après examens

- Meilleur affichage des cases.
- Multi-joueurs en local.
- Affichage de la liste des constructions possibles dans les villes.
- Affichage approché de la population des villes sur la carte (option).


Changement de la formule du calcul de hauteur des cases (et autres éléments de terrain) :

var taille_case_vertical = (taille_case/2)+(taille_case/8);


La formule précédente était en fait fausse. Je l'avais donc mise en commentaires, et bien sûr il n'y avait donc plus de "height" dans le tag d'image, ce qui ralentissait évidemment le navigateur (quand on ne met pas les tags de taille, le navigateur doit le rechercher DANS le fichier d'image lui-même. C'est plus lent).


Il a aussi fallu que je redimensionne mes images, afin de les faire tomber toutes sur un compte juste, genre 320*200 pixels (rapport 1/6, en fait, car il faut compter la hauteur du sous-sol dans le lot).
Ça c'est lourd, mais c'est l'expérience qui rentre, et la preuve qu'il vaut mieux TOUT préparer lorsqu'on se lance dans un projet quelconque.


Autre amélioration, le jeu supporte à présent le multi-joueurs (en local. Comme je l'ai dit, je ne connais rien au multi par internet).
L'appui sur le bouton de passage de tour ne fait avancer au tour suivant que si tous les joueurs sont passés, sinon il fait simplement passer au joueur suivant.
Pour l'instant, chaque joueur (humain) peut tout faire.


Dans le panneau de ville, la liste des batiments s'affiche.


Toujours dans le panneau de ville, le nom des armées constructibles s'affiche (le combat est basé sur des armées composées de bataillons).


J'ignore si je l'avais déjà mis en place avant les examens, mais l'affichage de la population des villes sur la carte peut aussi se faire de manière approchée :

if (carte_ville_pop_type == 1) {var b = ville_population[a];} else {var b = Math.floor(Math.log(ville_population[a]/carte_ville_pop_desc[0])/Math.log(carte_ville_pop_desc[1]));}

La valeur approchée consiste, grosso-modo, à afficher la population non sous sa valeur exacte, mais avec un nombre symbolique (comme dans les "civilization"). J'utilise les logarithmes, pour ça.




mercredi 6 juin 2012

Examens terminés !

Oh mon dieu, c'est terminé !

J'ai trouvé la dernière épreuve la plus stressante de toutes, bizarrement. En informatique, une matière où j'ai 20 de moyenne et 19,5 aux "partielles" (à cause d'une étourderie. Grr).

Mais je ne sais pas : Je me suis retrouvé bloqué sur une histoire de logique bête. J'avais oublié que non(x)=faux peut se simplifier en x=vrai (pour les opérateurs logiques).

Oui, c'est bête, mais ça s'appelle le stress, les enfants. C'est le truc en octobre on avait déjà vu.

Heureusement, comme j'ai fini le reste avec un quart d'heure de rab (prog de tableur, requêtes SQL, tout ça...), je suis revenu dessus : Ça me désespérait vraiment trop de perdre 3 pts sur 20 alors que je pensais avoir fait un sans faute pour le reste (j'ai quand même un doute sur deux points, alors mettons 18/20, sauf étourderie ?).

Et puis là, la lumière : Bon sang, mais c'est bien sûr !
Ou, dit autrement : 'tain, y'a sûrement une loi qui interdit d'être con comme ça...

Et j'ai résolu la question.


N'empêche, c'était stressant.

Mine de rien, maintenant que c'est terminé, je trouve que c'était même agréable de stresser comme ça.


----


Allez, encore un jour ou deux pour se remettre, et on va se remettre à Ratsodie (demain, moi y'en a fêter ça avec d'autres camarades de classe).

jeudi 10 mai 2012

Préparation d'examens

Salut,

Je pense mettre une mise à jour dimanche prochain. Le jeu a encore bien avancé, mais je veux finir le passage des joueurs avant ça.
Ce qui m'a permis d'ajouter, finalement, le multijoueurs. C'était assez simple : Il suffisait de mettre une variable pour l'IA (0 = pas d'IA = joueur humain) pour chaque joueur, et ça roule.


Au passage, j'ai été voter à l'élection. J'ai voté Marine le Pen.

Pas honte. Fier d'avoir assumé.
Et pas de leçons de morale à recevoir des nostalgiques d'une idéologie qui a assassiné plus de 150 millions de personnes au XXe siècle et à ceux qui les considèrent malgré celà comme de bons représentants de la démocratie.

jeudi 19 avril 2012

Garder les données en mémoire

Ayant multiplié les scripts comme d'autres les pains, je me suis retrouvé face à un autre problème : Comme on garde les variables, en fait ?

La solution que j'ai trouvée, est toute simple : Je les mets en attribut des images présentes sur l'écran.

En effet, on peut donner n'importe quel attribut à un élément HTML (au moins les images). Il faut juste éviter de donner un nom "réservé" (c'est à dire qui à un sens pour le navigateur lorsqu'il le trouve comme attribut).

Un navigateur qui ne reconnaît pas un attribut se contente de l'ignorer.
J'ai donc placé comme attribut aux villes des choses comme leur nom, le joueur auxquelles elles appartiennent, leur population, etc.

Sur le bouton de passage de tour, j'ai mis des données plus générales (comme le tour en cours, bien sûr).

Ensuite, comme je sais évidemment où je place quoi, il suffit à un script en ayant besoin d'aller chercher l'attribut voulu.

Par exemple :

    document.getElementById('case8[2]').setAttribute('bloquer','1');

Au début du script se chargeant d'afficher la "fenêtre" de ville, cette commande change l'attribut "bloquer" du bouton de passage de tours ('case8[2]', c'est son nom, pour le moment).

Quand le joueur clique sur le bouton de sortie de cette fenêtre, le script commence par :

    document.getElementById('case8[2]').setAttribute('bloquer','0');

En clair, il remet l'attribut "bloquer" du bouton de passage de tour à 0.

À quoi ça sert ?

Eh bien quand le joueur clique sur le bouton de passage de tour, son script fait ça :

    var bloquer = document.getElementById('case8[2]').getAttribute('bloquer');
    if (bloquer =='0')
    { reste du script }

Il lit la valeur de l'attribut "bloquer", toujours au même endroit, et la place dans une variable, simplement appelée "bloquer" aussi.

Puis il vérifie si "bloquer" vaut 0. Si tel est le cas, il exécute le reste du script, sinon ben il va tout de suite à la fin du "if", et là il n'y a plus rien, donc le script s'arrête.

En pratique, du point de vue du joueur tout au moins, celà désactive le bouton de passage de tour lorsque la fenêtre des villes est affichée.


mardi 17 avril 2012

Gloire à la Nuée !

Quand j'ai voulu écrire le code pour le passage des tours, j'ai naturellement écrit ça à la suite du code précédent, dans le même script.

Au début, ça marchait bien : Les tours passaient, une "alert box" suffisant à arrêter le jeu entre chaque tour.

Et puis j'ai ajouté du code pour remplacer l' "alert box" par une pause automatique entre les tours, le jeu passant au tour suivant par clic sur une image directement sur la page.

Et c'est là que ça s'est compliqué : Javascript n'a pas de système de pause intégré.
J'ai donc mis un pause "infinie" (un while avec une condition fausse par défaut).

Une interception d'évènement étant sensée changer la condition en "vrai" si le joueur appuyait sur le bouton de passage de tour.

Ah ouais mais non... Ça ne marche pas comme ça, finalement : Non seulement, apparemment, un script en cours ne lit pas les évènements, ce qui veut dire qu'on ne peut pas lui faire prendre en compte un clic, mais en plus les navigateurs ne supportent pas les scripts qui durent trop longtemps, à commencer par les boucles "infinies".

Moi y'en a être bien embêté.

Alors j'ai fini par me résoudre à ne pas faire un seul script monolitique, mais un par action désirée.
Finalement, ça marche aussi bien : Le script originel (et non "original", bande d'anglomanes !) se charge de créer la carte et de mettre en place tous les éléments du jeu, un autre script intercepte les clics sur le bouton de passage de tour et... fait passer le tour, un troisième script ouvre une "fenêtre" spéciale lorsqu'on clique sur une ville, et voilà.

Finalement, c'est même probablement plus simple.

lundi 16 avril 2012

Tours et fenêtre de ville


Ratsodie a bien avancé. C'était dur. En particulier, j'ai eu des surprises avec les CSS.

Figurez-vous que le positionnement des éléments (via CSS, donc) dépend de l'élément dans lequel ils se trouvent. Par exemple, une image placée dans un "div" dépend du positionnement du div.

Jusqu'ici, rien d'incompréhensible.

Ouais... Sauf que ça veut dire que si l'élément parent (ici le "div") ne possède pas de position déclarée comme telle, il y a des chances qu'un ou plusieurs éléments "enfants" (ici l'image) ne se positionne pas correctement.
Par exemple, dans mon cas, alors que toute la carte s'était correctement installée, les deux derniers éléments affichés par le script (une image et un texte) ne s'empilaient pas correctement : Le texte apparaissait en dessous de l'image, alors que leur z-index spécifiait clairement le contraire.

Et le plus drôle : Quelque soit le z-index spécifié, les deux éléments en question s'empilaient correctement avec les autres éléments.
En clair, je pouvais avoir la carte par dessus l'image, elle-même par dessus le texte, et ce dernier par dessus la carte (ce qu'on pouvait constater en les faisant déborder les uns des autres)...

J'ai eu la réponse sur le même forum que la dernière fois (merci Rtrethewey et Eye for Video) : Il me fallait préciser dans le DIV qui contenait mes deux derniers éléments (bref, leur parent) un posisitionnement, z-index compris.

Tu parles d'une chierie...



Enfin, quoi qu'il en soit, voilà l'évolution du moment :

Passage des tours
- En appuyant sur le gros bouton vert (piqué sur OpenCliparts, pour le moment), on fait avancer le tour, qui est lui indiqué en haut à droite.
- On peut spécifier le premier tour.
- Ainsi que le dernier.
- L'incrément.
- S'il y a un tour 0 (sinon, le jeu ajoute l'incrément).
- S'il y a un numéro d'ère (avant et après le "point zéro" du "calendrier"), et ce que c'est (genre "av. JC" et "ap. JC").
- Le bouton de tour peut se placer où le veut le moddeur.

Population
- En dessous du numéro de tour, est indiquée la population totale de l'empire du joueur. Ce nombre n'est pas recalculé à chaque tour, mais seulement lorsque quelque chose change (ce qui signifie que, pour le moment, il ne change pas du tout...)

Villes
- En cliquant sur une ville, on ouvre une fenêtre : Celles des villes.
- Pour le moment, cette fenêtre n'indique que le nom de la ville.
- En bas à droite (également plaçable ou le souhaite le moddeur), un bouton de sortie (tout aussi également piqué sur Openclipart, avant que je crée les miens). En cliquant dessus, on ferme la fenêtre (en fait, on appelle une fonction qui détruit le DIV de la fenêtre de ville).



La prochaine fois, j'espère avoir ajouté les premiers aménagements de ville (les constructions, quoi).

dimanche 1 avril 2012

Villes, population et civilisation

Voici les mises à jour du moment :

- Les villes ont à présent un nom.

Ce nom s'affiche au dessus de l'image de la ville, dans un cadre coloré.

À côté, certaines villes ont une étoile. C'est le symbole de capitale.

En dessous de la ville, un autre cadre, qui indique la quantité de population présente.

Je n'ai pas réellement cherché à centrer ces cadres ; on verra plus tard.


Pendant que j'y étais, j'ai introduit le principe des races (des camps, des civilisations, etc.).

En clair, j'ai ajouté une section "civilisations" à la partie "mod" du script. Pour le moment, cette section n'indique que la couleur de chaque civilisation, comme ça :

// Civ1
civ_couleur[1] = "00FFFF";

// Civ2
civ_couleur[2] = "FFFF00";

Ensuite, chaque joueur (humain ou machine) possède une civilisation :

joueur_civ[1] = 1;
joueur_civ[2] = 2;

Celà signifie, ici, que le joueur 1 joue une civilisation de numéro 1, et le joueur 2 une civilisation numéro 2.
Comme vous vous en doutez, plusieurs joueurs peuvent jouer la même civilisation, mais je ne me suis pas encore penché sur le moyen de les différencier sur la carte.

Ensuite, pour les villes :

ville_position[1] = "5*3";
ville_nom[1] = "Ville 1";
ville_joueur[1] = 1;
ville_capitale[1] = 1;
ville_population[1] = 10000;

Pour le moment, donc, une ville est définie par sa position sur la carte, son nom, le joueur à qui elle appartient, si elle est une capitale (1 = oui), et sa population.



Je tiens à remercier Padonak (et Trethewey), du forum "webdeveloper", pour leur aide au sujet de l'affichage de texte via javascript : innerHTML permet d'ajouter du text dans un "createElement".

mercredi 21 mars 2012

Afficher les villes

Bon. Il est temps de passer à la vitesse supérieure : Les villes.

Pour commencer, j'ai créé une image de ville toute simple, faite de petits cubes avec des portes : Ça peut représenter assez facilement une ville antique sous les pays chauds, genre mésopotamie, ce qui est très pratique puisque c'est là qu'elles sont sensé être apparues.

J'appelle cette image "ville.svg".

Maintenant, dans la partie "mod" du script, j'ajoute deux villes :

Eeeeeeet... le couper_coller via la souris n'est pas pris en compte par google-blogger. Ça fait rien, ils feront comme Apple et diront que c'est une inovation quand ils l'ajouteront). Heureusement, reste  CTRL+C (j'ai eu peur que ça ne fonctionne pas, après tout on est sous Linux).


// Ville préplacées
var ville_position = new Array();
var ville_nom = new Array();

ville_position[1] = "5*3";
ville_nom[1] = "Ville 1";

ville_position[2] = "6*13";
ville_nom[2] = "Ville 2";


Dans la variable "ville_position", je mets la position comme pour les rivières, c'est le plus simple.
Dans "ville_nom", je mets le nom de la ville.

Pour l'afficher sur la carte, un petit coup de

// Afficher les villes préplacée.

if (ville_position.indexOf(fleuve_element) != -1)
{
var ncase = "ville.svg";
case4[i3+"*"+j3] = document.createElement("img");
case4[i3+"*"+j3].src=mod+"/terrain/"+ncase;
case4[i3+"*"+j3].setAttribute('width',taille_case);
//case4[i3+"*"+j3].setAttribute('height',taille_case_vertical);
case4[i3+"*"+j3].setAttribute('id','case4['+i3+'*'+j3+']');
document.getElementById('ratsodie').appendChild(case4[i3+"*"+j3]);
case4[i3+"*"+j3].style.position = 'absolute';
case4[i3+"*"+j3].style.bottom = i2 + 'px';
case4[i3+"*"+j3].style.left = j2 + 'px';
case4[i3+"*"+j3].style.zIndex = j4+1;
}


Bref, même chose que d'habitude.

Jusque là, ça baigne, même si j'avais commencé par mélanger les case2, 3 et 4 (sinon, ça ne serait pas drôle), et bien sûr j'avais oublié de créer un tableau vide "case4" d'abord. Grr.


Prochains grands travaux : Afficher le nom de la ville au dessus de l'image.

Vous les voyez, les deux petites villes ?
Bon, ben j'ai réussi à télécharger l'image via Firefox (c'est vexant, mais quand ça veut...)


samedi 17 mars 2012

Optimisation des images

Plus j'avance, plus je me dis que l'utilisation de graphismes vectoriels pour les images lourdes et fréquentes (par exemple, les forêts) est une mauvaise idée.

Celà ne semble apporter qu'une bonne réaction au zoom, mais pour le reste les navigateurs souffrent comme c'est pas permis. Et comme il semble impossible à un javascript de modifier quoi que ce soit dans l'affichage d'une image SVG externe (genre, les couleurs), ça me fait une carte de plus en plus lourde, qui me fait planter la plupart des navigateurs.
Quand Firefox est le meilleur navigateur, c'est qu'il y a vraiment un problème...

Avant de voir s'il y a lieu de passer au bitmap (et l'absence de programme capable de gérer convenablement la transparence des PNG), j'ai commencé à voir si je ne pouvais pas améliorer l'affichage.

J'ai commencé par optimiser les scripts SVG. Une simple image peut ainsi passer de près de 500 ko à moins de 5 en virant le verbiage d'écureuil sous acide d'Inkscape. Le seul problème est qu'il faut penser à chaque fois à l'empêcher de sauvegarder en "svg inkscape" après toute nouvelle image.

'sont fous ces mecs là.

Ensuite, j'irai jeter un oeil à la transformation svg->bitmap avec mise en cache directement par le navigateur, dont j'ai entendu parler. Si ça marche vraiment, celà pourraît résoudre mon problème.

samedi 3 mars 2012

Ordre d'affichage des arbres

Ratsodie, pour afficher les forêts, divise chaque case en neuf, et met, nous l'avons vu, quelques arbres dans chaque case s'il y a lieu.

J'ai donc réalisé neuf images de forêts, chacune équipée d'arbres au niveau d'un neuvième de la taille d'une case de terrain. Ensuite, dans le code, une boucle de 1 à 9 affichait chaque morceau de forêt.

Mon problème est que je n'ai pas trop réfléchi, et j'ai fait s'afficher les morceaux de forêt dans cet ordre :

 1 - 4 - 7
 2 - 5 - 8
 3 - 6 - 9

Si vous avez lu ma dernière bafouille, vous pouvez vous rendre compte : Les arbres situés à l'est s'affichent par dessus ceux à l'ouest, C'est assez peu visible sur les images du blog, mais celles du jeu sont plus grandes, et là ça saute aux yeux.

Pour réparer, j'ai été au plus simple : Plutôt que modifier le code afin de lui faire afficher les images dans un autre ordre, ce qui s'avérait pour moi assez compliqué (contrairement au terrain, il n'y a qu'une seule boucle pour les forêts), j'ai simplement renommé les images de forêts afin de les faires coïncider avec l'ordre d'affichage souhaité.

En clair, les images ouest sont passées à l'est et vice-versa : 1, 2 et 3 sont devenues 7, 8 et 9, et inversement.

jeudi 1 mars 2012

L'ordre d'affichage des cases

Pour afficher les cases de la carte, Ratsodie s'y prend ainsi :


Boucle de 1 à nombre_de_lignes_de_la_carte
     Boucle de 1 à nombre_de_colonnes_de_la_carte
          Affichage de la case.
     Fin de boucle des colonnes
Fin de boucle des lignes

En clair, le jeu place la case Nord-Ouest, puis chaque case de la même ligne vers l'est.
Arrivé en bout de ligne, il recommence avec la ligne suivante, jusqu'à la dernière case de la carte, c'est à dire la case Sud-Est.

Le problème est l'index de profondeur : Chaque fois que le navigateur affiche une image, il la place par dessus les précédentes.
Celà peu paraître anodin, mais puisque Ratsodie affiche ses cases d'ouest en est, celà signifie que les cases situées à l'est sont au dessus des cases à l'ouest.

Bien sur, celà veut également dire que les cases au sud sont au dessus des cases au nord, mais ça c'est bien, puisqu'en 3D isométrique le sud est sensé être plus près du joueur : Normal donc qu'une case au sud puisse, éventuellement, en cacher une autre au nord.

Par contre, l'ouest est sensé être plus près du joueur que l'est. Donc si une case en cache sa voisine de l'ouest, ça ne va pas paraître visuellement confortable (en gros, l'effet 3D est détruit).

En quoi une case peut-elle cacher sa voisine ? Il y a déjà plusieurs possibilités dans Ratsodie à son niveau actuel de développement :

- Les forets dépassent des cases.
- Le fond des mers est plus bas que le niveau des cases.
- La partie "souterraine" des cases est également plus bas.

Exemple : Si on laisse Ratsodie afficher ses cases d'ouest en est, on va voir la partie souterraine des cases s'afficher par dessus la case immédiatement à l'ouest, alors qu'elle devrait être cachée par celle-ci.

J'avais trouvé la solution, que je connaissais déjà en partie à vrai dire : Le z-index.

En clair, chaque fois qu'un navigateur affiche quelque chose, il lui attribue un indice de profondeur (le z-index). Z, parce que que X pour la largeur, Y pour la hauteur et... Z pour la profondeur. Simpleme convention en géométrie.

Or, chaque nouvel objet affiché a un z-index supérieur au précédent (de 1, j'imagine).

Et vous savez quoi ? Le Z-index est un attribut comme les autres, et on peut donc dire au navigateur de donner un z-index différent à un objet qu'on veut afficher, exactement comme on peut le forcer à lui donner une taille ou un emplacement différent.

Je commence par calculer le z-index de la case que je veux afficher :

var j4 = (i*table_c)+(table_c-j);

En clair, pour chaque case, :

- Je prends le numéro de la ligne en cours (ici, i).
- Je multiplie ce numéro par le nombre de colonnes de la carte.
- Je prends encore le nombre de colonnes de la carte (ici, j).
- Et je lui enlève le numéro de la colonne en cours.
- J'ajoute les deux résultats.
- Ça me donne le z-index de la case en cours

Celà me donne des cases avec un z-index ressemblant à ceci :

4  -  3  -  2  -  1
8  -  7  -  6  -  5

De cette manière, les cases les plus lointaines (Est et Nord) ont bien un z-index inférieur à leurs voisines de l'Ouest et du Sud, et seront donc affichées en dessous.
Celà permet aux arbres de ne pas être coupés par la case juste au dessus, par exemple.

Ensuite, évidemment, j'ajoute le code suivant à la partie affichant l'image :

            case1[i3+"*"+j3].style.zIndex = j4;

L'image créée le sera donc avec un attribut de style "zindex" de valeur "j4", qui a été calculée juste avant.
















samedi 25 février 2012

Pas de moldus chez Harry Potter

Dernièrement, jetant un oeil aux statistiques du blog, je vois que certains visiteurs viennent d'un site français dédié à Harry Potter.

Curieux de savoir ce qu'on y dit de Ratsodie, je suis le lien et tombe sur un site pas très clair, où même la FAQ nécessite une inscription (un autre lien m'apprend qu'il s'agit d'une sorte de jeu en ligne).

La page d'où viennent les visiteurs étant cachée derrière l'inscription, je m'inscris. Jusqu'ici, rien que de très normal, je pense ?

Eh ben non. Je dois être un moldu : Non seulement la page est cachée à un "niveau d'administration supérieur", mais le temps de sortir faire une course et je m'aperçois que j'ai reçu un courriel m'indiquant d'un message très sec que mon compte a été annulé, et que si je veux savoir pourquoi, eh ben je n'ai qu'à voir ça avec un administrateur.

...

Heu, les mecs, faites gaffe quand-même : C'est exactement le genre d'attitude qui pousse nombre de webmasters à demander des filtres de liens entrants pour les scripts et services de blogs.
Ça serait dommage, mais quand on parle de quelqu'un sur un site internet, on évite de jeter la personne en question comme un malpropre lorsqu'elle vient voir.

jeudi 23 février 2012

Différenciation des forêts selon le terrain

L'intéret d'avoir des éléments qui sont des "terrains de base" et d'autres qui ne le sont pas (fleuves et forêts, pour le moment) se trouve dans la possibilité de s'en servir pour différencier les élements "non de base" selon le terrain.

Par exemple, celà permet de placer des forêts différentes sous les tropiques et dans l'arctique, sans avoir à le préciser dans la carte des forêts : On se contente d'indiquer la présence d'une forêt, et Ratsodie affiche la forêt correspondante au terrain.

Trois types de forêt différents

Pour se faire, j'ai créé deux autres lots d'images "morceaux de forêt" : Un pour les pays chauds, un autre pour les pays froids. Et on garde la forêt normale pour les climats tempérés.

Oui, je sais que les sapins ont une gueule pas possible... Ce sont des sapins russes, radioactifs (c'est vrai que j'en ai rarement vu d'aussi moches, mais bon, ça ira pour le moment).

Ensuite, j'ajoute dans le code, partie "mod", ce qui suit :

var ter_foret_image = new Array();
ter_foret_image[11]="";
ter_foret_image[12]="taiga";
ter_foret_image[13]="taiga";
ter_foret_image[14]="oceanique";
ter_foret_image[15]="oceanique";
ter_foret_image[16]="oceanique";
ter_foret_image[17]="tropical";
ter_foret_image[18]="tropical";
ter_foret_image[1]="";
ter_foret_image[2]="";
ter_foret_image[3]="";
ter_foret_image[4]="";
ter_foret_image[5]="";


On place ici, entre les guillemets, le nom de la forêt utilisée pour chaque type de terrain. Vous remarquerez que certains terrains n'ont pas de forêt : La mer ou la glace, par exemple.


Je nomme tous mes fichiers de forêt suivant ce principe :

foret_nomdelaforet_1.svg

"foret_" indique le type d'image, ici une forêt.

"nomdelaforet" est à remplacer par le... nom de la forêt. Ici, taiga, oceanique ou tropical.

"_1" est le numéro de chaque partie de forêt, et va donc de "_1" à "_9".

Encore une fois, pour le moment, l'image doit être une "svg" (vectorielle). J'arrangerais le code plus tard pour accepter n'importe quel type d'image.


Pour l'exemple, voici la liste des images de forêt :

** Bon, ben, impossible de télécharger l'image. Le bidule "flash" tourne en boucle et puis rien. Encore un truc au point, je vois **


Quoi qu'il en soit, dans la boucle d'affichage des forêts, la valeur "ncase", qui contient le nom de l'image, est modifiée afin de pouvoir contenir un nom d'image existant :

ncase = mod+"/terrain/"+"foret_"+ter_foret_image[table[i][j]]+"_"+k+".svg";

Le principe est simple : Quand le jeu voit qu'il doit afficher une foret, il prend le numéro du type de terrain de base de la case en question, cherche quel nom de forêt y correspond, et affiche les neuf images de forêt correspondant.

samedi 11 février 2012

Multiples terrains de base

Woah, allez...

Je sais bien que ce n'est pas primordial, et que j'avais dit vouloir me limiter, pour commencer, à ce qui est réellement nécessaire au jeu pour terminer, mais avouez qu'un jeu du royaume avec un seul terrain, c'est triste.
Alors voilà :



Les "nouveaux terrains

Comme vous pouvez le constater, il y a sur terre maintenant huit terrains différents. Les arbres de la forêt sont suffisamment espacés pour permettre au terrain d'être bien vu au travers.

Maintenant, le principal problème, c'est qu'avec le code actuel de Ratsodie il n'est pas possible pour un moddeur de mettre plus de terrains de base que prévu directement dans le code, parce que leur nombre est limité par la quantité de "if else" disponible.

J'ai réfléchi au problème, et en ai déduit que placer les terrains dans un tableau permettrait de résoudre le problème : Un tableau se remplit à volonté, et le code n'y prend que ce qui l'intéresse.

Première chose, lister les terrains dont j'ai (moddeur) besoin :

// Liste des terrains de base
var ter_image = new Array();
ter_image[11]="glace";
ter_image[12]="toundra";
ter_image[13]="taiga";
ter_image[14]="lande";
ter_image[15]="oceanique";
ter_image[16]="steppe";
ter_image[17]="desert";
ter_image[18]="tropical";
ter_image[1]="cote";
ter_image[2]="mer";
ter_image[3]="ocean";
ter_image[4]="lac";
ter_image[5]="lave";

Les valeurs entre crochets désignent le numéro d'identification que je veux donner à chaque terrain. Ce sont ces numéros que j'utilise dans le tableau de cartes :

// Carte du monde. Un terrain de base.
   table1 = new Array(11,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18);
   table2 = new Array(11,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18);
   table3 = new Array(11,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18);
   table4 = new Array(11,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18);
   table5 = new Array(11,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18);
   table6 = new Array(11,11,11,12,04,04,04,14,14,15,15,16,16,17,17,18,18);
   table7 = new Array(11,11,11,04,04,13,13,14,14,15,15,16,16,17,17,18,18);
   table8 = new Array(11,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18);
   table9 = new Array(11,11,11,12,12,13,13,14,14,15,15,16,16,17,17,18,18);
table10 = new Array(11,01,01,12,12,13,13,14,14,15,15,16,16,17,17,18,18);
table11 = new Array(01,01,11,12,12,13,13,14,14,15,15,16,16,05,05,05,18);
table12 = new Array(11,11,11,12,12,13,01,14,14,15,15,16,16,17,05,05,18);
table13 = new Array(11,01,01,01,01,01,01,01,01,01,15,16,16,17,17,18,18);
table14 = new Array(01,01,02,02,02,02,02,02,02,02,01,01,16,17,17,18,18);
table15 = new Array(01,02,02,02,02,03,03,03,03,02,02,02,01,01,01,18,18);

Vous remarquerez que j'ai enlevé les guillemets qui entouraient les valeurs : Il m'est apparu que, puisque ce seront toujours des nombres, ils ne servaient à rien.

Puisque je place les terrains dans un tableau, je peux en faire autant de la variable "mer". En effet, elle était définie jusque là dans la série de "if" définissant également l'image de terrain à afficher.
Comme çà, plus besoin de ces "if" du tout.

Je vous rappelle que, pour le moment, une case ayant plus de 0 n'affiche ni les fleuves, ni les forêts sur cette case, même si leurs tables respectives disent le contraire.

// Liste des terrains étant considérés comme une "mer". 1 est de l'eau classique, 2 de la lave. D'autres valeurs peuvent indiquer des types "aquatiques" différents.
var ter_mer = new Array();
ter_mer[1]=1;
ter_mer[2]=1;
ter_mer[3]=1;
ter_mer[4]=1;
ter_mer[5]=2;

Pendant que j'y étais, j'ai également enlevé les guillemets autour des valeurs de la carte des forêts.  C'est toujours ça de gagné.

// Carte des forêts. 1=Oui, 0=Non. La variable "mer" a priorité sur ce tableau.
foret1 = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
foret2 = new Array(1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0);
foret3 = new Array(1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0);
foret4 = new Array(1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0);
foret5 = new Array(1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0);
foret6 = new Array(1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0);
foret7 = new Array(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1);
foret8 = new Array(1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0);
foret9 = new Array(1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0);
foret10 = new Array(1,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0);
foret11 = new Array(0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0);
foret12 = new Array(0,0,1,1,1,1,0,1,1,1,1,1,0,0,0,0,0);
foret13 = new Array(0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0);
foret14 = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
foret15 = new Array(0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0);
Voilà pour la partie "mod" du script.

Ensuite, pour la partie "code", ce sera plus simple.

D'abord,

        var mer=0;

disparait. Je n'en ai plus besoin, après tout.

Ensuite, les lignes

  if (table[i][j]=="18") { ncase = "nom_de_mon_terrain.svg"; }

disparaissent également, puisque le jeu sait déjà à quelle image correspond une valeur de case donnée.
Elles sont remplacées par la ligne suivante :

ncase = "terrain_"+ter_image[table[i][j]]+"_1.svg";    

Cette ligne reconstruit le nom du fichier d'image voulu et le place dans la variable "ncase".

Enfin, le "if" d'affichage des fleuves prend en compte maintenant "ter_mer" et non plus "mer", évidemment. Même chose pour la forêt :

        if (fleuve.indexOf(fleuve_element) != -1 && !ter_mer[table[i][j]])
        if (foret[i][j]==1 && !ter_mer[table[i][j]])


Le résultat :

- Je peux maintenant faire une carte avec autant de terrains de base que je le veux.
- Le jeu me semble plus rapide, probablement gràce à la suppression de tous ces "if else".


Ah oui : Pour que celà marche, il faut suivre une nomenclature précise pour les images. Exemple :

terrain_nomdemonterrain_1.svg

Pour un désert, on mettra "terrain_desert_1.svg"

"Terrain" pour signifier que c'est un terrain (de base). Pour le moment, il n'a pas d'autre intérêt que de mieux répérer les images de terrain dans le répertoire.

La valeur à la fin sera utilisée pour autoriser les graphismes multiples pour un terrain (je verrais ça plus tard).

Pour le "svg", je verrais également plus tard si l'extension de l'image doit être indiquée dans le mod ou directement cherchée par le script. Je ne sais pas encore.








dimanche 5 février 2012

Faire accepter les mods par le jeu

Vu le travail de cette semaine (un seul jour de congé : Aujourd'hui dimanche, et encore pas mal de boulot "à la maison" - et noté, qui plus est), j'ai fait très court.

Un de mes buts principaux est de rendre Ratsodie parfaitement modable. Vu que javascript n'a jamais été prévu pour lire ou écrire des fichiers (il y a bien les activeX, mais sous Linux, c'est un coup à invoquer un démon sous ma chaise).

Le principal problème que je rencontre est que je voudrais que le script en cours lise des données du genre "carte=oui" dans le fichier ciblé, et mette ça dans une variable pour que la suite du script l'utilise (un autre problème est d'obliger le script js à attendre que la lecture du fichier externe soit terminée, et là c'est comme demander la lune apparemment).

Je suis en train de jeter un oeil à AJAX/JSON. Après tout, si JS peut demander une action à un serveur, il me semble logique qu'il puisse se contenter de demander un fichier texte déjà créé, non ?

Grr.

Enfin, quoiqu'il en soit, La "modabilité" de Ratsodie commence par le plus simple : Il est désormais possible de charger des graphismes différents selon le mod.


D'abord, j'ai commencé par concentrer tout ce qui concerne précisément les données du mod au début du script, avant le moindre calcul : Cartes des terrains, des forêts, liste des fleuves, taille nord-sud et est-ouest de la carte, et taille désirée pour l'affichage de la carte.

Ce sont toutes ces données que je cherche à mettre dans un fichier à part, que Ratsodie chargerait au début du jeu.


Ensuite, une variable indiquant le nom du mod désiré :

var mod="nomdumod";

(tiens, Blogger ne met plus le code de mise en forme autour du texte, mais directement le résultat. C'est bien plus pratique, faut admettre).

nomdumod correspond au nom du répertoire où se trouve le mod. Le jeu "standard" livré avec Ratsodie s'appelant simplement "Ratsodie", le répertoire où il se trouve s'appelle également "ratsodie".


Ensuite, le code de Ratsodie proprement dit.

Pour forcer Ratsodie à aller chercher les images dans le bon répertoire, les lignes indiquant le src (source) ont été modifiées comme suit :

case1[i3+"*"+j3].src=mod+"/terrain/"+ncase;

En clair, devant "ncase" (qui contient le nom de l'image à afficher), je rajoute la variable "mod", qui contient le répertoire du mod en question, et le texte /terrain/, qui ajoute entre les deux le nom du sous-répertoire du même nom.

Je préfère, en effet, ranger les différents graphismes dans des sous-répertoires distincts, afin d'éviter d'avoir au final des milliers d'images différentes au même endroit.








dimanche 29 janvier 2012

Donc...

Donc, voilà où j'en suis à l'heure où je crée ce blog. J'essaierai de progresser un peu chaque semaine. Ça dépendra du temps qu'il me restera.

La prochaine fois, je pense ajouter les collines.


Les forêts - Affichage

Maintenant que nous avons réglé notre problème, voilà comment Ratsodie affiche ses bouts de bois.

D'abord, je crée un tableau :

kf = new Array();

Ensuite, juste après l'accolade fin de l'affichage des rivières, j'ajoute ceci :

if (foret[i][j]==1 && mer != 1)
{
for (var k=1;k<10;k++)
{
if (kf[k]==0)
{
ncase = "foret_"+k+".svg";
case3[i3+"*"+j3] = document.createElement("img");
case3[i3+"*"+j3].src=ncase;
case3[i3+"*"+j3].setAttribute('width',taille_case);
//case3[i3+"*"+j3].setAttribute('height',taille_case_vertical);
case3[i3+"*"+j3].setAttribute('id','case3['+i3+'*'+j3+']');
document.getElementById('ratsodie').appendChild(case3[i3+"*"+j3]);
case3[i3+"*"+j3].style.position = 'absolute';
case3[i3+"*"+j3].style.bottom = i2 + 'px';
case3[i3+"*"+j3].style.left = j2 + 'px';
case3[i3+"*"+j3].style.zIndex = j4+1;
foretoui=0;
}
}
}


Rien d'extraordinaire, comme vous le voyez. Je pense que la plus grande partie de ce code d'affichage d'image pourrait aller dans une fonction que les parties pour le terrain, les rivières et la forêt appelleraient en lui passant "ncase, i2, j2, i3, j3 et j4 (les coordonnées).

Je veux dire, dès que je saurais si on PEUT faire des fonctions en javascript...

À vrai dire, je ne sais pas s'il est vraiment utile de donner une variable différente à chaque image, et s'il ne serait pas plus simple d'appeler tout ça "image" plutôt que caseX[blablabla+"*"+blablabla], et point à la ligne. J'essaierai ça.

Le code commence par vérifier qu'il y a bien une forêt dans la case, mais pas la mer.

Ensuite, on fait une boucle pour les neuf parties de case.
Pour chacune, on vérifie que son kf est bien à 0 (si une autre partie de Ratsodie a mis cette valeur à 1, cela veut dire "pas de forêt ici")

Oh, et ne pas oublier :
Le tableau "kf" sert à indiquer s'il y a une forêt dans chaque sous-case (1 à 9).
Il faut donc le vider à chaque nouvelle case affichée. Or, apparemment, il n'existe pas de moyen simple pour vider un tableau (le javascript doit avoir été prévu pour des scripts de toute petite taille au début de sa carrière. Il n'est pas dit qu'on ait prévu qu'on puisse un jour avoir besoin de réutiliser un tableau).

Donc, je fais comme ça :

            for(var k2=0;k2<10;k2++) {kf[k2]=0;}

que j'ajoute juste juste au début de la boucle "J". Ça met toutes les valeurs du tableau à 0.

Dernier point :

kf[5]=1;
if (fleuve.indexOf(fleuve_element_n) != -1) {fleuve_voisin+=1;
kf[4]=1;}
if (fleuve.indexOf(fleuve_element_e) != -1) {fleuve_voisin+=2;
kf[8]=1;}
if (fleuve.indexOf(fleuve_element_s) != -1) {fleuve_voisin+=4;
kf[6]=1;}
if (fleuve.indexOf(fleuve_element_o) != -1) {fleuve_voisin+=8;
kf[2]=1;}

La partie ajoutée (au début du code affichant les fleuves) est en rouge.


En clair, quand il y a un fleuve dans la case, Ratsodie vérifie s'il y en a aussi un dans une case voisine afin de les joindre. Si tel est le cas, il ne peut pas y avoir de forêt dans la sous-case où va passer le cours d'eau, aussi Ratsodie passe-t'il la valeur de kf[numéro de la partie de case/forêt] à 1, ce qui sera vu lorsqu'il sera temps d'afficher la forêt comme un "pas d'arbres ici".

Et, bien entendu la case kf 5, qui est au centre, ne peut jamais avoir d'arbres s'il y a un fleuve dans la case, puisque tous les fleuves passent toujours par le centre de la case.



Une case qui dépasse

Si vous avez l'oeil, vous avez pu remarquer, dans la liste des bouts de forêt, que certaines images sont plus grandes que d'autres :


La numéro 7, par exemple, est visiblement plus haute.

En fait, c'est parce qu'un problème, auquel je n'avais pas pensé, m'est tombé sur le crâne.
J'avais, fort logiquement, commencé par afficher les images de manière tout à fait normale, en leur donnant des coordonnées. En clair, je mettais leurs coordonnées sur leur style LEFT et TOP. Gauche et Dessus.

Ça paraissait logique, jusqu'à ce que je comprenne que non, en fait.

Parce que, vous l'imaginez, une image a peu de raisons de dépasser sa case par le dessous, d'autant qu'il y a déjà un sous-sol inclu, et que Ratsodie affiche les cases plus au sud par dessus (il commence par le coin Nord-Ouest de la carte, puis fait chaque ligne l'une après l'autre jusqu'à arriver au coin Sud-Est).

Par contre, les raisons de dépasser par le dessus (le Nord, quoi) sont nombreuses. Le premier que j'ai vu fut la forêt : Si un arbre pousse tout en haut de la case, son sommet va, logiquement, dépasser le haut de la case.

Et cela sera la même chose avec les montagnes, les villes, etc.

Comment faire ?

Parce que le problème, voyez-vous, ce n'est pas qu'une image soit plus grande que la case où elle est sensée s'afficher. C'est que puisque Ratsodie affiche les images par leur haut ("style.top"), comme on fait habituellement, il va mettre le haut de l'image en haut de la case... et repousser tout le reste de l'image vers le bas.

Je ne sais pas si je suis bien clair, alors un petit dessin :


Dans le premier cas, vous voyez un petit damier de six cases.
Comme vous pouvez le constater, la case rose, ayant la même taille que ses voisines, s'insère parfaitement dans le damier.

Dans le second damier, vous pouvez voir que la case rose est plus haute. Mais comme Ratsodie l'affiche en l'alignant par le haut (style.top=coordonnée de l'image), l'image est repoussée vers le bas, ce qui n'est pas ce que je veux puisque c'est le sommet des arbres qui dépasse, pas leurs racines !

Moi, ce que je veux, c'est comme dans le troisième damier : Ratsodie aligne l'image sur ses voisines, mais par le bas ! Et ainsi, si quelque chose doit dépasser, ce sera par le haut.

La solution m'a tarabusté un petit peu, jusqu'à ce que je trouve ceci : On PEUT aligner une image par le bas. Au lieu de "style.top", on fait "style.bottom", et voilà.

C'est bête, mais encore fallait-il le savoir.



Les forêts - principe

Pour les forêts, deux choix possibles s'offraient à moi.

Tout d'abord, je pouvais créer un terrain "forêt" : Une case pouvait être une plaine déboisée OU une forêt, par exemple.

Mais il m'était également possible de considérer la forêt comme un ajout à la case, comme le cours d'eau.

C'est cette dernière solution que j'ai choisi. Non qu'elle soit forcément la meilleure (elle occupe plus de mémoire), mais parce qu'elle réduit le nombre de "terrains de base", et je préfère séparer les torchons et les serviettes.

La forêt est indiquée dans un tableau exactement comme le terrain de base :

foret1 = new Array("1","1","1","1","1","1","1","1","1","1","1","1");
foret2 = new Array("1","1","1","1","1","1","1","1","1","1","1","1");
foret3 = new Array("1","1","1","1","1","1","1","1","1","1","1","1");
foret4 = new Array("1","1","1","1","1","1","1","1","1","1","1","1");
foret5 = new Array("1","1","1","1","1","1","1","1","1","1","1","1");
foret6 = new Array("1","1","1","1","1","1","1","1","1","1","1","1");
foret7 = new Array("1","0","0","1","1","1","1","1","1","1","1","1");
foret8 = new Array("0","0","1","1","1","1","1","1","1","1","1","1");
foret9 = new Array("0","0","1","1","1","1","0","1","1","1","1","1");
foret10 = new Array("0","0","0","0","0","0","0","0","0","0","1","1");
foret11 = new Array("0","0","0","0","0","0","0","0","0","0","0","0");
foret12 = new Array("0","0","0","0","0","0","0","0","0","0","0","0");
foret = new Array(foret1,foret2,foret3,foret4,foret5,foret6,foret7,foret8,foret9,foret10,foret11,foret12);


Ensuite, je n'oublie pas un petit

case3 = new Array();

comme pour les cours d'eau.


Un autre problème, ennuyeux celui-là, m'apparu : La forêt, on la place par dessous ou par dessus la rivière ?

Non, parce que si la forêt est dessus, on ne va plus voir la rivière.
Et si elle est dessous, franchement, on aura l'air de quoi avec notre rivière volante ?

La solution que j'ai trouvé est toute simple, et à vrai dire j'y avais pensé au moment où de travaillais sur les cours d'eau (j'étudiais les deux en même temps).

Il s'agit de diviser chaque case en neuf parties. Parties purement virtuelles bien entendu.

Suivez-moi bien :
J'ai déjà dit que Ratsodie regarde non seulement s'il y a une rivière dans la case qu'il affiche, mais également s'il y en a dans les cases Nord, Est, Sud et Ouest. Il suffit de faire couler la rivière au centre de la carte, et on peut dès lors diviser toute case en neuf parties, dont la "croix centrale" peut être occupée par une rivière.

Restait à réaliser neuf cases affichant neuf bouts de forêt. Je ne me suis pas fatigué, et les neuf bois sont identiques à part leur emplacement sur la case :



Cours d'eau et océans

Pour ce qui est de la jonction des cours d'eau et de la mer (ou d'un lac), j'avais la possibilité de faire comme dans les jeux "Civilization", c'est à dire que lorsqu'une rivière arrive dans une case voisine de la mer, elle ne va pas plus loin et la rejoint via une embouchure quelconque.

J'ai personnellement voulu laisser le choix au créateur de carte de décider où le cours d'eau rejoint la mer. Cela permet, par exemple, de faire couler un fleuve non loin de l'océan : Si vous prenez une carte du monde, vous découvrirez qu'à l'échelle de la planète, beaucoup de cours d'eau coulent près d'une mer sans forcément s'y jeter : L'Amazone et le Pacifique, le Nil et la mer Rouge, etc.

Ce que j'ai fait est donc qu'un fleuve ne coule que vers la case où se trouve un autre fleuve. Pour rejoindre la mer, ou un lac, il suffit donc de le faire couler DANS la mer. Puis j'ai interdit à Ratsodie d'afficher un cours d'eau si la variable "mer" vaut 1, c'est à dire si la case en question est une étendue d'eau.

Cours d'eau


Pas de belle carte sans cours d'eau. Non seulement ils sont un embellissement graphique non négligeable, mais ils aident à se repérer et peuvent également être très utiles : La présence d'un fleuve a toujours été primordiale dans l'établissement de nouvelles villes.

Des cours d'eaux meublent tout de suite une carte.
Les cours d'eau sont affichés après le terrain de base. Eux aussi sont directement dans le javascript, pour la même raison que le terrain :

fleuve = new Array("1*3","2*3","3*3","3*4","5*4","5*5","6*5","7*5","8*5","8*6","8*7","8*8","8*9","9*9",
"10*9","3*10","3*9","4*4","4*9","5*9","6*9","6*8","6*7","6*6","3*11","3*12","2*11",
"4*11","4*7","4*8","5*1","5*2","5*3","4*2","1*4","1*5","1*6","1*7","1*8","2*6","2*1");

Il n'y a qu'un seul tableau, et il est monodimensionnel. Chaque élément du tableau indique une coordonnée de la carte (d'abord la ligne, puis la colonne, un asterisque - qui à ma grande suprise est un mot masculin. On apprend tous les jours - servant à les délimiter).

Par exemple, le premier élément est "1*3". Celà veut dire qu'il y a un cours d'eau ligne 1, colonne 3

On utilise pour l'instant les éléments suivants :

Seize éléments pour les cours d'eau.
Il est fort probable que j'en rajoute par la suite, ne serait-ce que pour les embouchures (estuaires, deltas, etc).

EDIT : J'ai renommé certains depuis la prise de cette image. Les images dont la valeur commence par un 0 ont perdu celui-ci (sinon, beh Ratsodie ne les trouve pas).


Le principe d'affichage, maintenant :

Chaque fois que Ratsodie affiche une case de terrain, il regarde s'il y a une entrée correspondante dans le tableau "fleuve" (cours d'eaux).

Si c'est le cas, il examine les six cases voisines Nord, Est, Sud, Ouest, et regarde s'il y a un cours d'eau dedans.
  • Nord vaut 1 point ;
  • Est vaut 2 points ;
  • Sud vaut 4 points ;
  • Ouest vaut 8 points ;
Ensuite, il fait l'addition, et le total est également le numéro de l'image de cours d'eau (riviere_X.svg, où X est un nombre), qu'il affiche par dessus le terrain.

Je commence donc par définir

case2 = new Array();
J'ignore si je peux reprendre le "case1" du terrain, simplement.


Puis, juste après le code affichant mon terrain, mais avant de refermer l'accolade, je fais ça :

var fleuve_element = i3+"*"+j3;
if (fleuve.indexOf(fleuve_element) != -1 && mer !=1)
{
var fleuve_element_n = (i3-1)+"*"+j3;
var fleuve_element_e = (i3)+"*"+(j3+1);
var fleuve_element_s = (i3+1)+"*"+j3;
var fleuve_element_o = (i3)+"*"+(j3-1);
var fleuve_voisin = 0;
  if (fleuve.indexOf(fleuve_element_n) != -1) {fleuve_voisin+=1;}
  if (fleuve.indexOf(fleuve_element_e) != -1) {fleuve_voisin+=2; }
  if (fleuve.indexOf(fleuve_element_s) != -1) {fleuve_voisin+=4; }
  if (fleuve.indexOf(fleuve_element_o) != -1) {fleuve_voisin+=8; }

var ncase = "riviere_"+fleuve_voisin+".svg";


case2[i3+"*"+j3] = document.createElement("img");
case2[i3+"*"+j3].src=ncase;
case2[i3+"*"+j3].setAttribute('width',taille_case);
//case2[i3+"*"+j3].setAttribute('height',taille_case_vertical);
case2[i3+"*"+j3].setAttribute('id','case2['+i3+'*'+j3+']');
document.getElementById('ratsodie').appendChild(case2[i3+"*"+j3]);
case2[i3+"*"+j3].style.position = 'absolute';
case2[i3+"*"+j3].style.bottom = i2 + 'px';
case2[i3+"*"+j3].style.left = j2 + 'px';
case2[i3+"*"+j3].style.zIndex = j4;

}

Ce qui devrait faire (si je ne me suis pas gouré dans le couper-coller) l'image au début de ce message.


Affichage de la carte

Maintenant qu'on a une carte, le principal soucis est : Comment l'afficher.
Après pas mal de tâtonnements, voilà où j'en étais, à peu près :


case1 = new Array();

for(var i=0 ; i < table_l ; i++)
{
for(var j=0 ; j < table_c ; j++)
{

var i2 = (table_l*taille_case/4)-i*(taille_case/4)+(j*(taille_case/4));
var j2 = 50+(j*taille_case-(j*taille_case)/2)+(i*taille_case/2);
var i3 = i+1;
var j3 = j+1;
var j4 = (i*table_l)+(table_c-j);

var mer=0;

// Afficher le sol

if (table[i][j]=="11") { ncase = "tempere_01.svg"; }
else if (table[i][j]=="01") { ncase = "mer_01.svg"; mer=1; }
else if (table[i][j]=="02") { ncase = "mer_02.svg"; mer=1; }
else if (table[i][j]=="03") { ncase = "mer_03.svg"; mer=1; }
else if (table[i][j]=="04") { ncase = "lac_01.svg"; mer=1; }
else if (table[i][j]=="05") { ncase = "lave_01.svg"; mer=1; }

case1[i3+"*"+j3] = document.createElement("img");
case1[i3+"*"+j3].src=ncase;
case1[i3+"*"+j3].setAttribute('width',taille_case);
//case1[i3+"*"+j3].setAttribute('height',taille_case_vertical);
case1[i3+"*"+j3].setAttribute('id','case1['+i3+'*'+j3+']');
document.getElementById('ratsodie').appendChild(case1[i3+"*"+j3]);
case1[i3+"*"+j3].style.position = 'absolute';
case1[i3+"*"+j3].style.bottom = i2 + 'px';
case1[i3+"*"+j3].style.left = j2 + 'px';
case1[i3+"*"+j3].style.zIndex = j4;


 }}



Comme je l'ai déjà écrit, ça (doit) donne(r) ça :

La variable "mer" est là pour indiquer à Ratsodie de ne pas y planter de choses comme des arbres. C'est juste une facilité quand on trace la carte des forets.

Codage de la carte



J'ai quelques vagues idées sur ce que je voudrais comme résultat d'une génération aléatoire de carte, mais pour le moment aucune idée sur la manière de programmer ça.

Donc, je me suis contenté d'une carte pré-créée.

Les données de la carte se trouvent directement dans le code JS. J'ignore encore comment faire lire (et filtrer) un fichier externe par javascript, donc voilà :

table1 = new Array("11","11","11","11","11","11","11","11","11","11","11","11");
table2 = new Array("11","11","11","11","11","11","11","11","11","11","11","11");
table3 = new Array("11","11","11","11","04","04","04","11","11","11","11","11");
table4 = new Array("11","11","11","04","04","11","11","11","11","11","11","11");
table5 = new Array("11","11","11","11","11","11","11","11","11","11","11","11");
table6 = new Array("11","11","11","11","11","11","11","11","11","11","11","11");
table7 = new Array("11","01","01","11","11","11","11","11","11","11","11","11");
table8 = new Array("01","01","11","11","11","11","11","11","11","11","11","11");
table9 = new Array("11","11","11","11","11","11","01","11","11","11","11","11");
table10 = new Array("11","01","01","01","01","01","01","01","01","01","11","11");
table11 = new Array("01","01","02","02","02","02","02","02","02","02","01","01");
table12 = new Array("01","02","02","02","02","03","03","03","03","02","02","02");


Je commence par entrer le terrain de base pour chaque carte. "11" signifie par exemple le sol, 03 l'océan, etc.
Je place chaque ligne dans un tableau séparé, parce que c'est plus lisible.


table = new Array(table1,table2,table3,table4,table5,table6,table7,table8,table9,table10,table11,table12);

var table_l=12;
var table_c=12;


Maintenant, j'indique le nombre de lignes de la carte ("table_l"), c'est à dire sa hauteur Nord-Sud, et le nombre de colonnes ("table_c"), c'est à dire sa largeur Est-Ouest, et je place chaque ligne de la carte dans une vaste table appelée Table (!), qui est donc un tableau multidimensionnel ("table" contient tous les "tableX", qui contiennent chacun les cases de leur ligne).

Mon principal problème ici est que je voudrais bien utiliser la variable "table_l" pour, à l'aide d'une boucle quelconque, genre "for", entrer toutes les lignes automatiquement dans Table sans avoir à faire ce long "new array" avec les "table1, table2, table3 ... tableN" dedans.
Imaginez-vous le bordel avec une carte de 200 lignes ?

Une idée ?




Outils de programmation

Ah oui, je vois que j'ai omis de préciser avec quoi je veux réaliser celà.

Ben, d'abord, pour me simplifier la tâche au maximum, c'est un jeu par navigateur. C'est à dire que c'est le navigateur internet (IE, Opera, Firefox, Safari... Ce que vous voulez) qui s'occupe de l'affichage.
Parce que c'est une grosse part de la programmation dont je n'ai pas à m'occuper. Réinventer la roue, j'aime pas trop.

Ensuite, c'est un jeu entièrement "côté client". C'est à dire que le serveur n'a pas besoin de faire tourner quelque code que ce soit.
Les avantages sont, pour moi, les suivants :
  1. C'est beaucoup plus facile à programmer ;
  2. Ça coûte moins cher, parce que les serveurs faut les louer ;
  3. Beaucoup moins de problèmes de sécurité, puisque le jeu ne sort pas de chez le joueur.
Les langages utilisés sont le javascript, parce qu'il est relativement simple et, au final, fait ce qu'on lui demande du moment qu'on ne cherche pas à réaliser un programme destiné à concurrencer World of Warcraft.
Qui plus est, il y a plein de gens qui l'utilisent, et la plupart des navigateurs internet sont maintenant munis d'une console d'erreurs qui permet de voir plus ou moins où on s'est trompé.

Je n'ai jamais utilisé de "framework", et honnêtement je n'ai pas l'intention de me lancer là dedans.

Pour mon codage, je le fais dans Geany, une sorte de super bloc-notes qu'on trouve sous Linux, mais j'imagine que l'équivalent doit se trouver sous Windows ou Mac.
J'aime bien Geany parce que, lui non plus, y fait pas chier. Il me met les numéros des lignes, me souligne les accolades ouvrantes et fermantes, et me colore différement les différents éléments du code (genre variables, mots-clés, etc.). À part ça, il y a un bouton "enregistrer" qui m'enregistre directement mon fichier sans m'em... nuyer avec un quelconque "projet", un bouton "ouvrir" quand je veux l'inverse, il comprend le CTRL-Z pour effacer ma dernière ânerie, la touche TAB fonctionne, et voilà tout.
Quand on a besoin de plus, c'est qu'on ne fait déjà plus dans ce genre de projets.

Pour réaliser mes dessins, hélas sous Linux c'est un peu la cata. Heureux les macosiens et windoziens qui ont Photoshop. Nous on a Gimp.
Fermons la parenthèse.

Pour réaliser des dessins vectoriels, qui sont après tout l'idéal quand on veut faire du dessin au trait et des aplats de couleur (les graphismes de Ratsodie sont en vectoriel, mais il peut utiliser n'importe format reconnu par les navigateurs. C'est juste que moi je les fais en vectoriel), j'utilise Inkscape.
Pas que je sois emballé, mais Illustrator sous Linux, on n'aura jamais faut pas rêver (ouais, je sais, il y a wine. L'émulateur qui officiellement n'en est pas un et cesse de fonctionner d'une version à l'autre. Merci du cadeau).
Inkscape permet de faire du vectoriel, donc. Il est encore très récent (ce qui, en mode "logiciel libre", signifie qu'il a moins de deux mille ans), et donc pour trouver la moitié des fonctions il faut essayer de trouver quel raccourci-clavier à trois touches la permet parce que le bouton ou même l'accès via le menu, ben même Livingstone le cherche encore.
Et ton "read the fucking manual", tu te l'enfonces dans la partie la plus intelligente de ton individu et tu y mets le feu. Tu l'as lu, ton manuel ?

Pour le reste, un navigateur pour tester et pour la console d'erreurs.


Terre et Mer

Ma première tâche fut de séparer la terre de la mer, comme toute divinité qui se respecte.

J'ai choisi de donner une épaisseur à mes cases, sans trop me soucier de ce qui serait affiché ou pas, pour deux raisons :

D'abord, pour ne pas trop compliquer et mon travail, et le code, pour un projet de grand débutant. Vouloir trop en faire dès le début est un moyen assez sûr de ne jamais obtenir quelque chose de jouable avant d'en être dégoûté et d'abandonner.
Beaucoup de projets ont terminé comme ça.

Ensuite, celà me permet de placer des cases plus basses, ou avec transparence (comme l'eau), sans me soucier d'avoir à ajouter un bord opaque pour les cases situées derrière. Du point de vue du code, le temps perdu à dessiner des parties de cases souvent invisibles pour le joueur (on ne voit pas le sous-sol lorsqu'il y a un terrain normal devant) est probablement assez bien rattrapé par l'absence de conditions programmées du genre "si je place tel type de case derrière tel autre type, est-ce-qu'on voit le sous-sol derrière, et si oui comme le dessine-t'on ?"

Terre et Mer

Pour commencer, j'ai réalisé quatre cases basiques : Une de terre et trois de mer (le lac est venu plus tard, mais le principe est identique).
C'est le terrain "de base" - tout le reste : Forêts, rivières, unités, villes... - viendra se placer par dessus, mais on ne peut avoir qu'un seul terrain de base par case.

Vous remarquerez que j'ai utilisé de la transparence pour l'eau : C'est un moyen simple de différencier la mer de la terre, qui elle est opaque. De plus, le fond de la mer est au même niveau que le point le plus bas du sous-sol de la terre, ce qui donne une bonne impression de profondeur à mon avis.

Je n'ai pas cherché non plus à multiplier les graphismes de côtes, avec des caps, des baies, des falaises, des plages ou je ne sais quoi : Si on y réfléchi, c'est assez "accessoire" et peut être ajouté plus tard.

En fait, tout ce dont l'absence ne gêne pas le jeu peut-être considéré comme accessoire, et être ajouté plus tard, selon l'humeur du moment, ce qui permet de se concentrer sur l'essentiel : Que le jeu fonctionne.

Isométrie, rejeton des enfers.

Voilà le premier jet :

Première carte du jeu
Isométrique :

Bon, je ne rentrerai pas dans le débat de savoir si c'est de l'iso 2D, de la 3D, voire de la 2.5D (y'a pas de raisons). Pour moi, c'est de l'isométrique, et de toutes façons la "vrai 3D" actuelle n'en sera plus dès qu'on voudra nous vendre une nouvelle technique. Et puis, comme on dit, la 3D sur écran plat...

Ceci dit, j'en ai bavé pour comprendre comment faire de l'iso. Celà me semblait une bonne idée, parce que visuellement la carte donne ainsi une bonne impression de profondeur, sans pour autant rendre les cases lointaines illisibles parce que, comme vous le savez, ce qui est loin apparaît plus petit.

Ça a été l'horreur. D'abord, je suis une brêle absolue en javascript. Ça ne devrait pas être permis d'être aussi mauvais. En fait, je n'avais pas la moindre idée de la manière de m'y prendre, et j'ai du comprendre comment placer les cases dans la bonne position.

En fait, si vous connaissez déjà la question, vous devez savoir qu'une case isométrique n'est pas limitée à la partie visible, en "diamant". C'est en fait une case parfaitement rectangulaire, invisible (transparence = 100%), où l'on dessine la partie visible, puis on place ces rectangles en "tuile", débordant les uns sur les autres afin de faire coïncider les parties visibles.

Isométrique
Une autre chose à ne pas oublier est que des cases isométriques sont alignées plus ou moins en diagonale (c'est ce qui donne l'impression de profondeur), et non pas verticalement ou horizontalement.
Quand on a jamais fait ça, ni réellement programmé quoi que ce soit en javascript, c'est l'horreur.






Lancement du projet Ratsodie

Celà fait longtemps que je veux réaliser un jeu du royaume, c'est à dire un jeu où l'on gère un domaine quelconque (oui, c'est le nom originel de ce genre de jeux). Eh bon, après environ 25 ans, me voilà à l'ouvrage.

J'ai donc décidé de commencer par concevoir une carte, à la "civilization", très classiquement.