Un jeu de logique en Flash

Licence

Ce code est fourni sous une licence CC-By (cf. fin de page).
De plus, il est interdit de recompiler le jeu "tel quel" en supprimant le lien «Plus de jeux», la mention «tel quel» restant à l'appréciation de l'auteur original du code source (copie, plagiat...).

À propos du code

Le code est réparti en classes claires et nettes.
L'ensemble du fichier représente moins de 10ko !

La position de chaque noeud est calculée à la volée, à l'aide d'une méthode récursive plutôt performante.

Classe principale A-Graphe

Fichiers de bases :

Code source : Agraphe.as
//Code original par Neamar. Réutilisation libre.
//neamar@neamar.fr

//Ligne de commande pour compiler :
//(après s'être placé dans le bon dossier, cf. http://neamar.fr/Res/Compiler_AS3/)
//
//bash mxmlc Sources/A-graphe/Agraphe.as -default-size 640 480 -compiler.strict

//Life begins when you can spend your spare time programming instead of
//watching television.

package
{
import flash.display.Sprite;//La classe de base, fort utile !
import flash.events.*;//Chargons tous les évenements ce sera plus simple :)
import flash.geom.Point;//Un point tout ce qu'il y a de plus normal : défini par (x,y).
import flash.text.TextField;//Champ de texte. Classe assez vaste, utilisée uniquement pour l'affichage de texte ici

import flash.filters.BitmapFilter;//Classe globale pour les filtres
import flash.filters.GlowFilter;//Filtre d'irridescence (éclairage, le plus utilisé)
import flash.filters.BlurFilter//Flouter
import flash.filters.BevelFilter;

import flash.net.URLRequest;//Prépare une requete.
import flash.net.sendToURL;//Execute une requete sans récuperer la réponse
import flash.display.Loader;//Chargement d'un fichier normal, et récupère le contenu
import flash.system.LoaderContext;//Forcer le chargement du crossdomain.xml
import flash.net.SharedObject;//Sauvegarde locale



[SWF(width = "640", height = "480", frameRate = 25)];//Quelques paramètres pour la compilation. Ils sont cependant redéfinis dans la ligne de commande.

public class Agraphe extends Sprite
{
public var Fond:Niveau;//Fond est un Sprite Niveau, qui contient le niveau en cours.

public const VERSION:String="RC1";//La version
public var Img_Fond:Loader = new Loader();

public var SAVE_LOCAL:SharedObject;//L'enregistrement du niveau en cours, et des niveaux unlockés.
public var SESSION:Object;//Un raccourci vers SharedObject.datas

public var NumeroNiveauActuel:int=0;
public var NumeroNiveauUnlockes:int;


private var GrandPere:Noeud; //GrandPere est présent dans chaque niveau, c'est le noeud final à atteindre.

function Agraphe():void
{
// Les constantes primitives utilisées tout au long du Flash
const FlashWidth:int = 640;
const FlashHeight:int = 480;

// Récuperer les données locales
SAVE_LOCAL = SharedObject.getLocal("AGraphe");
SESSION = SAVE_LOCAL.data;

// SESSION.NumeroNiveauUnlockes=SESSION.NumeroNiveauActuel=11;
// SESSION.NumeroNiveauActuel=SESSION.NumeroNiveauUnlockes=null;

if(SESSION.NumeroNiveauActuel==null)
SESSION.NumeroNiveauActuel=SESSION.NumeroNiveauUnlockes=NumeroNiveauActuel;//On ne met pas à 0 afin de permettre des tests : lors du débuggage, il suffit de changer une variable pour passer à un niveau.

NumeroNiveauUnlockes=SESSION.NumeroNiveauUnlockes;//Recharger les dernières valeurs
NumeroNiveauActuel=NumeroNiveauUnlockes;//Commencer au dernier niveau

// include "../Trace.as";
include "ChangementsNiveaux.as";//Pendant le niveau, test si il est fini. Prépare le passage à un nouveau niveau.
include "ChargementNiveaux.as";//La liste des niveaux, les différentes possibilités de le charger (via un String, un Array...)
include "Message.as";//La boite de message. Montre un message via la méthode ShowMessage(message:String)
include "2Joueurs.as";//Gestion du mod «2 Joueurs»
include "Persos.as";//Gestion du mod «Créateur de niveaux»
// include "Resolution.as";//Non inclus dans la version distribuée. Teste un niveau, renvoie le nombre minimal de noeuds à allumer.

SwapBackGround();//Afficher l'image de fond

//Enregistrer le nouveau joueur :
sendToURL(new URLRequest("http://neamar.fr/Res/AGraphe/Player.php"));

Fond = GetLevelData(NumeroNiveauActuel);//Charger le niveau de numéro NumeroNiveauActuel
addChild(Fond);
Fond.graphics.clear();//enlever le fond blanc que les niveaux ont normalement.
Fond.FlouFinal=Fond.FLOU_FINAL;

//Repasser la boite de message au premier plan...c'est plus pratique pour la lire :-)
setChildIndex(Fond,1);//Sinon, Fond est au 1er plan et bloque la lecture des informations.
addEventListener(Event.ENTER_FRAME,TesterFinNiveau);


////////////////////////////////
//Les éléments inclassables
////////////////////////////////
function SourisIN(e:Event):void
{//Fonction Utilisée sur tous les sprites de type bouton : ajoute un filtre au Sprite afin de l'illuminer
AppliquerFiltre(e.currentTarget)
}
function SourisOUT(e:Event):void
{//Fonction Utilisée sur tous les sprites de type bouton : retire les filtres du Sprite
e.currentTarget.filters=new Array();
}
function AppliquerFiltre(Element:Sprite,Couleur:uint=0x000000):void
{//Ajoute un filtre de rayonnement à l'objet spécifié
var FiltreHover:BitmapFilter = new GlowFilter(Couleur, 0.8,16,16,3,1,false,false);
var ListeFiltres:Array = new Array();
ListeFiltres.push(FiltreHover);
Element.filters=ListeFiltres;
}
function SwapBackGround():void
{//Télecharger l'image de fond, l'afficher.
//Le Img_FondContext sert à demander le télechargement du fichier crossdomain.xml
Img_Fond.load(new URLRequest("http://neamar.fr/Res/AGraphe/Images/Fond.png"),new LoaderContext(true));
if(!contains(Img_Fond))
{//Et la mettre au dernier plan
addChild(Img_Fond);
setChildIndex(Img_Fond,0);
}
}
function ResetAll(e:Event):void
{
NumeroNiveauActuel=NumeroNiveauUnlockes=SESSION.NumeroNiveauActuel=SESSION.NumeroNiveauUnlockes=0;
UpdaterBoite();
}
}
}
}
Code source : ChangementsNiveaux.as
/////////////////////////////////////
//Fonctions utilisées en cours de niveau
/////////////////////////////////////
function TesterFinNiveau(e:Event):void
{//Teste si le niveau est terminé, ou si il est invalide.
//Fonctionne aussi quand le mode 2 joueurs est activé.
if(Mode2Joueurs)
Verifier_2Joueurs();
else
{
if(GrandPere.Ferme)
TerminerNiveau(1);//GAGNÉ !
else
{
if(Fond.NbJetonsToleres-Fond.NbJetonsUtilises<0)
{//PERDU
TerminerNiveau(0);
ChangeLevelInfo.htmlText="<b>Trop de noeuds allumés !</b>";
}
}
}
}

/////////////////////////////////////
//Fonctions utilisées lors des changements de niveaux
/////////////////////////////////////
function TerminerNiveau(Valeur:int):void
{//Termine un niveau en affichant la boite de changement
//Ajoute la valeur aux niveaux unlockés.
removeEventListener(Event.ENTER_FRAME,TesterFinNiveau);

Flouter(Fond,0,Fond.FLOU_FINAL);//Ajouter un flou

NumeroNiveauActuel+=Valeur;
if(NumeroNiveauActuel>NumeroNiveauUnlockes && Fond.Officiel==true && NumeroNiveauActuel<12)
{//Si il vient de réussir le niveau sur lequel il était bloqué
NumeroNiveauUnlockes+=Valeur;//Débloquer un nouveau niveau
SESSION.NumeroNiveauActuel=NumeroNiveauActuel;//Et enregistrer en local la progression
SESSION.NumeroNiveauUnlockes=NumeroNiveauUnlockes;
ChangeLevelInfo.htmlText="Nouveau Niveau débloqué !";
}

//Puis afficher la boite avec la liste des niveaux.
UpdaterBoite();
}

function StopLevel(e:Event=null):void
{//Arrête le niveau directement.
TerminerNiveau(0);
Message_Container.visible=false;
ChangeLevelInfo.htmlText="<b>Niveau abandonné !</b>";
}

function LancerNouveauNiveau(NumNiveau:int=-1):void
{//Lancer un niveau "quelconque"
if(NumNiveau==-1)
NumNiveau=NumeroNiveauActuel;

if(Fond!=null && contains(Fond))
{//Si Fond a déjà été utilisé, nettoyer l'ensemble
Fond.Nettoyer();
removeChild(Fond);
}


//SwapBackGround();
Fond = GetLevelData(NumNiveau);
Fond.graphics.clear();//Supprimer le fond blanc
addChild(Fond);
//Repasser la boite de message au premier plan
setChildIndex(Fond,1);

Flouter(Fond,Fond.FLOU_FINAL,0);//Déflouter

addEventListener(Event.ENTER_FRAME,TesterFinNiveau);
ChangeLevelInfo.htmlText="<u>Liste des niveaux</u>";
}

////////////////////////////////////////
//Partie gérant les effets graphiques
////////////////////////////////////////
function Flouter(Layer:Niveau,Depart:int,Arrivee:int):void
{
Layer.FlouFinal=Arrivee;
Layer.Flou=Depart;
Layer.addEventListener(Event.ENTER_FRAME,GererFlou);
}

function GererFlou(e:Event):void
{//Flouter le niveau en arrière plan
var Layer:Object=e.currentTarget;
if(Layer.Flou!=Layer.FlouFinal)
{
if(Layer.Flou<Layer.FlouFinal)
Layer.Flou+=2;
else
Layer.Flou-=2;
var FiltreFlou:BitmapFilter = new BlurFilter(Layer.Flou,Layer.Flou)
var ListeFiltres:Array = new Array();
ListeFiltres.push(FiltreFlou);
Layer.filters=ListeFiltres;
if(Layer.FlouFinal==0)
{
Boite.alpha=Math.max(0,Layer.Flou/Layer.FLOU_FINAL);
if(Layer.Flou==0)
Boite.visible=false;
}
else
{
Boite.visible=true;
Boite.alpha = Math.min(1,2*Layer.Flou/Layer.FlouFinal);
}
}
else
{
Boite.visible=(Boite.alpha>.5);
Layer.removeEventListener(Event.ENTER_FRAME,GererFlou);
}
}
Code source : ChargementNiveaux.as
/////////////////////////////////////
//Fonctions de création de niveau
/////////////////////////////////////

function GetLevelData(Numero:int,Not_Mute:Boolean=true):Niveau
{
const NIVEAUX_JETONS:Array=new Array(3,4,5,4,5,5,3,5,6,7,5,6);
var Hook:Array=new Array(); //Vide la liste des Hook.
var Non_Extinctible:Array=new Array(); //Vide la liste des Non_Extinctible.
var Niveau_datas:Array; //Contient l'arborescence sous forme numérique
var Inutile:Array=null;//Evite de réecrire complètement un niveau lors de sa modification
var Message:String="";

function T(...Liste):Array
{//T comme Tableau
//Plus court à écrire que new Array() à chaque fois
return Liste;
}
if(Numero==0)
{
Niveau_datas=new Array(null,null,T(0,1));
Message ="<u>Règles d'A-graphe : <font size=\"-2\"><i>(partie 1/3)</i></font></u><br><li><b>Règle n°1 : </b> Pour allumer un noeud (representé par un cercle), cliquer dessus.</li><li><b>Règle n°2 : </b> Pour terminer un niveau, allumer le noeud principal, centré en haut.</li><li><b>Règle n°3 : </b> Un noeud est allumable si et seulement si tous ses enfants (les noeuds qui descendent de lui) sont allumés.</li>";
}

else if(Numero==1)
{
Niveau_datas=new Array(null,null,null,T(0,1,2),null,null,T(4,5),T(3,6));
Message = "<u>Règles d'A-graphe : <font size=\"-2\"><i>(partie 2/3)</i></font></u><br><li><b>Règle n°4 : </b> Il est interdit d'allumer simultanément plus de noeuds que le nombre imparti pour chaque niveau (ce nombre est affiché en haut, à droite).</li><li><b>Règle n°5 : </b> Une fois un noeud allumé, on peut éteindre ses enfants sans que cela éteigne le parent.</li>";
}
else if(Numero==2)
{
Niveau_datas=new Array(null,null,T(0,1),null,null,T(1,3,4),null,T(4,6),null,T(8),T(9),T(2,5,7,10));
Message = "<u>Règles d'A-graphe : <font size=\"-2\"><i>(partie 3/3)</i></font></u><br><li><b>Règle n°6 : </b> Un noeud allumé puis éteint ne peut pas être rallumé.</li><li><b>Règle n°7 : </b> Les noeuds marqués d'une croix ne peuvent pas être éteints.</li><br>Voilà, c'est tout pour les règles. Bonne chance !";
}
else if(Numero==3)
Niveau_datas=new Array(null,null,null,T(0),T(3,2),T(1,0),T(5,0,4));
else if(Numero==4)
{
Niveau_datas=new Array( null,null,null,T(0,1,2),null,null,T(4,5),T(3,6),null,null,T(8,9),null,null,T(11,12),null,null,T(14,15),T(10,13,16),null,T(17,18),T(7,19));
}
else if(Numero==5)
{
Niveau_datas=new Array(null,null,null,T(0),T(0,2),T(2),T(3,4,5),T(1),T(3,4,5),T(6,7,8));
Hook[1]=1;
Message = "<li><b>Dernier niveau du tutorial.</b> Les choses vont rapidement se gâter ! Bonne chance pour la suite...</li><li>Un conseil avant de nous quitter : pensez à bien vérifier l'agencement des noeuds avant de commencer : certains sont un peu traîtres...</li>";
}
else if(Numero==6)
{
Niveau_datas=new Array(null,T(0),null,null,T(3),T(2,3),T(4),T(5,6),T(1,7));
Hook[3]=-1;
}
else if(Numero==7)
{
Niveau_datas=new Array(null,T(0),T(0),T(0),T(1),T(1,2),T(2,3),T(3),T(4,5),T(5,6,7),T(8),null,T(9,11),T(10),T(12),T(13,14),T(13,15),T(14,11,7),T(16,15,17));
//Hook[15]=-1;
Non_Extinctible[7]=true;
}
else if(Numero==8)
{
Niveau_datas=new Array(null,T(0),null,T(0,2),T(1),T(4),T(1,0),T(3,2),T(5,4),null,null,null,T(9,10),T(10,11),T(12),T(13),T(14),T(15),T(12),T(13),T(10),T(16,18,20,19,17),T(6,7,8),T(21,22));
}
else if(Numero==9)
Niveau_datas=new Array(null,T(0),T(0),T(1),T(1,0),T(2,0),T(2),T(3),T(3,1,4),T(5,6),T(6),T(7,3,8),T(9,10),T(11,8,4,5,9,12));
else if(Numero==10)
Niveau_datas=new Array(null,null,null,null,T(0,1),T(2,3),T(4),T(4),T(5),T(5),T(6),T(6,7),T(7),T(8),T(9),T(10,11,12),T(13,14),T(15),T(15,16),T(16),null,null,null,null,null,T(22,20,23),T(23,21,24),T(25,26),T(17,18),T(18,19),T(28,29),T(30,27));
else if(Numero==11)
{
Niveau_datas=new Array(null,T(0),T(0),T(0),T(1,0),T(1,0,2),T(1,2,3),T(4,5,6),T(5,0,6),T(0,6),null,null,T(10),T(11),T(13,11),null,null,null,null,null,T(12),T(14),T(13,20),T(15,16,17,18),T(19,12,10),T(21,20),T(22,23,24),T(25),T(25),T(26,27),T(27),T(28),T(29),T(29,30),T(0,6,31,32,33),T(7,8,34));
Message="<u>Dernier niveau</u><br>Je vous souhaiterais bien bonne chance, mais je crains que cela ne suffise pas ;-)";
}

if(Message!="" && Not_Mute && !Mode2Joueurs)
ShowMessage(Message);
if(Mode2Joueurs)
ShowMessage("<u>Niveau en mode 2 Joueurs</u><li>Les informations du niveau sont affichées en haut, à droite.</li><li>N'oubliez pas que vous pouvez éteindre des noeuds ! Votre tour ne se finit que quand vous en allumez un.</li>");

return LoadNiveau(Niveau_datas,Numero.toString(),NIVEAUX_JETONS[Numero],Hook,Non_Extinctible);
}

function String2Level(Niv_String:String,Titre:String="Mon niveau perso",NbJetons:int=5):Niveau
{//Crée un niveau à partir d'un string.
//Utilisé en mode 2 joueurs, mais aussi lors de la résolution automatique d'un niveau.
var Hook:Array=new Array(); //Vide la liste des Hook.
var Non_Extinctible:Array=new Array(); //Vide la liste des Non_Extinctible.
var Allumes:Array=new Array();
var DejaAllumes:Array=new Array();
var i:int=0;
var Niv_Array:Array=Niv_String.split(",");
function ReplaceNullItem(T:Array):void
{

if(T.length>1)
{
for(var j:int=0;j<T.length;j++)
{//On ne peut pas utiliser for each, car String est un type primitif : on agirait sur une copie de l'objet.
if(T[j]=="null")
T[j]=null;
}
}
}


for(i=0;i<Niv_Array.length;i++)
{//Trouver les Hook, les allumés et les non exctinctibles.
if(Niv_Array[i]!=Niv_Array[i].replace("X",""))
{//ajouter NonExtinctible
Niv_Array[i]=Niv_Array[i].replace("X","");
Non_Extinctible[i]=true;
}
if(Niv_Array[i]!=Niv_Array[i].replace("@",""))
{//ajouter NonExtinctible
Niv_Array[i]=Niv_Array[i].replace("@","");
Allumes[i]=true;
}
if(Niv_Array[i]!=Niv_Array[i].replace("-",""))
{//ajouter un Hook
Niv_Array[i]=Niv_Array[i].replace("-","");
Hook[i]=-1;
}
if(Niv_Array[i]!=Niv_Array[i].replace("+",""))
{//ajouter un Hook
Niv_Array[i]=Niv_Array[i].replace("+","");
Hook[i]=1;
}
if(Niv_Array[i]!=Niv_Array[i].replace("!",""))
{//ajouter un Hook
Niv_Array[i]=Niv_Array[i].replace("!","");
DejaAllumes[i]=true;
}
}
//Remplacer "null" par...le vrai null
ReplaceNullItem(Niv_Array);

for(i=0;i<Niv_Array.length;i++)
{//On ne peut pas utiliser for each, car String est un type primitif
if(Niv_Array[i]!=null && Niv_Array[i].indexOf("/")!=-1)
{
Niv_Array[i]=Niv_Array[i].split("/");
Niv_Array[i].pop();//Supprimer le dernier item, qui est null
ReplaceNullItem(Niv_Array[i]);
}
}
return LoadNiveau(Niv_Array,Titre,NbJetons,Hook,Non_Extinctible,Allumes,DejaAllumes);
}

function ChargementNiveauDirect(e:Event):void
{//Triggeré quand clic sur une des miniatures dans Boite.
while(Boite.numChildren > 0)
Boite.removeChildAt(0);

Boite.addChild(ChangeLevelInfo);

ChangeLevelInfo.htmlText="<u>Chargement en cours</u> (niv. <b>"+e.currentTarget.NumeroNiveau+"</b>)";
if(e.currentTarget.Officiel)
{
NumeroNiveauActuel=e.currentTarget.NumeroNiveau;
LancerNouveauNiveau(e.currentTarget.NumeroNiveau);
}
else
LancerChargement();
}


function LoadNiveau( Level:Array,NumNiveau:String,NBJetons:int,Hook:Array,Non_Extinctible:Array,Allumes:Array=null,DejaAllumes:Array=null):Niveau
{//La fonction principale, qui charge le niveau dans un sprite
var Layer:Niveau; //Layer est le Sprite qui doit contenir le niveau
if(Allumes==null)
Allumes=new Array();
if(DejaAllumes==null)
DejaAllumes=new Array();
Layer=new Niveau(NumNiveau,Level,Hook,Non_Extinctible,Allumes,DejaAllumes,NBJetons,FlashHeight,FlashWidth,Mode2Joueurs);
GrandPere=Layer.All_Noeuds[Level.length-1];
//Puis régler les positions Y.
//Pour cela, il faut d'abord trouver le nombre d'imbrications.
//Facile ! Il suffit d'appeler GrandPere.Profondeur(), qui est récursive.
GrandPere.GererXY(1,GrandPere.Profondeur(),FlashWidth-20,FlashWidth/2 -10);
GrandPere.RecreerArbreLien();
Layer.CreerArbreParents(GrandPere);
return Layer;
}
Code source : Message.as

//Déclaration des Sprites
var Boite:Sprite = new Sprite();
var RetryLevelButtonInterne:Sprite;
var Annuler_Choix:Sprite;
var Neamar_Sprite:Sprite;

var Message_Container:Sprite=new Sprite();
var Message_TexteOK:Sprite=new Sprite();
var Mods:Sprite=new Sprite();
var LVL_En_Cours:Niveau;
var LVL_Edit:Niveau=null;

//Remplissage des Sprites

Message_Container.graphics.beginFill(0x444444);
Message_Container.graphics.lineStyle(2,0x000000);
Message_Container.graphics.drawRect(0, 0,440,140);
Message_Container.x=100;
Message_Container.y=160;
Message_Container.visible=false;
Message_Container.alpha=.8;

var Message_Texte:TextField = CreerTextField(Message_Container,10,10,420,100);

Message_TexteOK = LevelChangeButton(0);
Message_TexteOK.scaleX=Message_TexteOK.scaleY=.3;
Message_Container.addChild(Message_TexteOK);
Message_TexteOK.x=420;
Message_TexteOK.y=120;
Message_TexteOK.addEventListener(MouseEvent.CLICK,RemoveMessage);


Boite.graphics.beginFill(0xCCCCCC);
Boite.graphics.lineStyle(1,0x000000);
Boite.graphics.drawRect(0, 0,240,180);
Boite.x=50;
Boite.y=50;
Boite.width=FlashWidth-100;
Boite.height=FlashHeight-100;
Boite.visible=false;
addChild(Boite);
AppliquerFiltre(Boite);

Annuler_Choix=LevelChangeButton(180);
Annuler_Choix.scaleX=Annuler_Choix.scaleY=0.15;
Annuler_Choix.addEventListener(MouseEvent.CLICK,Annuler);

addChild(Message_Container);
AppliquerFiltre(Message_Container);


var ChangeLevelInfo:TextField = CreerTextField(Boite,30,0,FlashWidth-100,25,false,false,false);
ChangeLevelInfo.htmlText="<u>Liste des niveaux</u>";


RetryLevelButtonInterne=LevelChangeButton(90);
addChild(RetryLevelButtonInterne);
RetryLevelButtonInterne.x=640;
RetryLevelButtonInterne.y=450;
RetryLevelButtonInterne.scaleX=RetryLevelButtonInterne.scaleY=.2;
RetryLevelButtonInterne.addEventListener(MouseEvent.CLICK,StopLevel);


Neamar_Sprite = new Sprite();
Neamar_Sprite.graphics.beginFill(0xCCCCCC);
Neamar_Sprite.graphics.drawRect(0, 0, 640, 480);
Neamar_Sprite.graphics.endFill();
Neamar_Sprite.graphics.lineStyle(2,0x000000);
Neamar_Sprite.graphics.lineTo(640,0);
Neamar_Sprite.graphics.lineTo(340,220);
Neamar_Sprite.graphics.moveTo(0,0);
Neamar_Sprite.graphics.lineTo(320,220);
Neamar_Sprite.graphics.moveTo(320,260);
Neamar_Sprite.graphics.lineTo(0,480);
Neamar_Sprite.graphics.lineTo(640,480);
Neamar_Sprite.graphics.lineTo(360,260);
Neamar_Sprite.graphics.drawCircle(320, 240,120);
Neamar_Sprite.width=640;
Neamar_Sprite.height=480;
Neamar_Sprite.scaleX=Neamar_Sprite.scaleY=0.07;
AppliquerFiltre(Neamar_Sprite);
Neamar_Sprite.addEventListener(MouseEvent.MOUSE_OVER,APropos);
Neamar_Sprite.addEventListener(MouseEvent.CLICK,AProposTexte);

Mods.graphics.beginFill(0xCCCCCC);
Mods.graphics.drawRect(0, 0, 100,30);
Mods.graphics.endFill();
AppliquerFiltre(Mods);

//Ajout dans Mods
var SpriteReset:Sprite=new Sprite();
var Mods_Reset:TextField = CreerTextField(SpriteReset,0,0,180,20,false,false,false);
Mods_Reset.htmlText="<li>RESET</li>";
SpriteReset.scaleX=SpriteReset.scaleY=0.4;
SpriteReset.y=16;
Mods.addChild(SpriteReset);

SpriteReset.addEventListener(MouseEvent.MOUSE_OVER,SourisIN);
SpriteReset.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);
SpriteReset.addEventListener(MouseEvent.CLICK,ResetAll);

//Lien vers http://www.mini-jeu-gratuit.fr/
var SpriteSponsor:Sprite=new Sprite();

var Mods_Sponsor:TextField=CreerTextField(SpriteSponsor,0,0,50,35,false,true,false)
Mods_Sponsor.htmlText="<a href=\"http://www.mini-jeu-gratuit.fr/vip/neamar/\" target=\"_blank\">Plus de jeux</a>";
// SpriteSponsor.addEventListener(MouseEvent.MOUSE_MOVE,SourisIN);
// SpriteSponsor.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);



function CreerTextField(Parent:Sprite,x:int,y:int,width:int,height:int,multiline:Boolean=true, selectable:Boolean=false,border:Boolean=true):TextField
{//Instanciateur de TextField
var TextFieldTemporaire:TextField=new TextField();
Parent.addChild(TextFieldTemporaire);
TextFieldTemporaire.x=x;
TextFieldTemporaire.y=y;
TextFieldTemporaire.width=width;
TextFieldTemporaire.height=height;
TextFieldTemporaire.wordWrap=true;
TextFieldTemporaire.background =TextFieldTemporaire.border=border;
TextFieldTemporaire.multiline=multiline;
TextFieldTemporaire.selectable=selectable;
return TextFieldTemporaire;
}

function LevelChangeButton(Angle:int):Sprite
{//fonction qui génére un bouton de type "Suivant".
var LevelChangeButtonSprite:Sprite=new Sprite();
LevelChangeButtonSprite.graphics.beginFill(0x7F7FB3);
LevelChangeButtonSprite.graphics.lineStyle(1,0x000000);
LevelChangeButtonSprite.graphics.drawCircle(75,75,75)
LevelChangeButtonSprite.graphics.endFill();
LevelChangeButtonSprite.graphics.lineStyle(10,0xFF0000);
LevelChangeButtonSprite.graphics.moveTo(33,33);
LevelChangeButtonSprite.graphics.lineTo(116,75);
LevelChangeButtonSprite.graphics.lineTo(33,117);
LevelChangeButtonSprite.rotation=Angle;
LevelChangeButtonSprite.alpha=1;
LevelChangeButtonSprite.addEventListener(MouseEvent.MOUSE_MOVE,SourisIN);
LevelChangeButtonSprite.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);
return LevelChangeButtonSprite;
}

function ShowMessage(Texte:String):void
{//Affiche un message.
Message_Container.visible=true;
Message_Texte.htmlText=Texte;
}
function RemoveMessage(e:Event=null):void
{//Cache la boite de messages
Message_Container.visible=false;
}

function UpdaterBoite():void
{
function Afficher(Image:Niveau,Empl:int):void
{
Boite.addChild(Image);
Image.scaleX=Image.scaleY=0.07;
Image.x=15 + (Empl%4)*Ecart;
Image.addEventListener(MouseEvent.MOUSE_OVER,SourisIN);
Image.addEventListener(MouseEvent.MOUSE_OVER,AfficherInfos);
Image.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);
Image.addEventListener(MouseEvent.CLICK,ChargementNiveauDirect);
Image.y=20 + Math.floor(Empl/4)*Ecart/1.3;
}

RemoveMessage();
//Générer un préview des niveaux
while(Boite.numChildren > 0)
Boite.removeChildAt(0);

var Ecart:Number=Boite.width/10;
for(var i:int=0;i<=NumeroNiveauUnlockes;i++)
{
var ThumbShot:Niveau=GetLevelData(i,false);//true indique qu'il ne faut pas afficher les messages;
Afficher(ThumbShot,i);
Boite.addChild(ThumbShot);
if(i==NumeroNiveauActuel)
AppliquerFiltre(ThumbShot,0x7F7FB3)
}

if(LVL_Edit!=null)//LVL_Edit est mis à jour lors du chargement d'un niveau perso
Afficher(LVL_Edit,14);
else
{
Boite.addChild(SpriteSponsor);
SpriteSponsor.x=15+2*Ecart;
SpriteSponsor.y=20+3*Ecart/1.3;
SpriteSponsor.filters=new Array(new BevelFilter(7,90,0x7F7F00,1,0x000000,1,10,4,10));
}

Boite.addChild(Annuler_Choix);
Annuler_Choix.y=16;
Annuler_Choix.x=16;
Boite.addChild(ChangeLevelInfo);
Boite.addChild(Neamar_Sprite);
Neamar_Sprite.x=17 + 3*Ecart;
Neamar_Sprite.y=20 + 3*Ecart/1.3;

Boite.addChild(Mods);
Mods.x=15;
Mods.y=20 + 3*Ecart/1.3;

}

function AfficherInfos(e:Event):void
{
ChangeLevelInfo.htmlText="Charger le niveau <b>"+e.currentTarget.NumeroNiveau +"</b>";
if(e.currentTarget.NumeroNiveau==NumeroNiveauUnlockes)
ChangeLevelInfo.htmlText="Dernier niveau débloqué : <b>"+e.currentTarget.NumeroNiveau +"</b>";
if(e.currentTarget.NumeroNiveau==NumeroNiveauActuel)
{
ChangeLevelInfo.htmlText="Dernier niveau tenté : <b>"+e.currentTarget.NumeroNiveau +"</b>";
AppliquerFiltre(e.currentTarget,0x7F7FB3)
}
}
function APropos(e:Event):void
{
ChangeLevelInfo.htmlText="Informations, explications, code source...";
}

function AProposTexte(e:Event):void
{
ShowMessage("<u>À propos...</u><br><li>Jeu réalisé par Neamar en AS3 libre.</li><li>Explications du jeu, dernières mises à jour, code source, aide à la compilation : <a href=\"http://neamar.fr/Res/AGraphe/\" target=\"_blank\">http://neamar.fr/Res/AGraphe (click to go)</a></li>");
}

function Annuler(e:Event):void
{
Flouter(Fond,Fond.FLOU_FINAL,0);//Déflouter
GrandPere=Fond.All_Noeuds[Fond.All_Noeuds.length-1];//Remettre le bon GrandPere !
addEventListener(Event.ENTER_FRAME,TesterFinNiveau);
}

Fichiers de mod

Code source : 2Joueurs.as

//Les variables utiles :
var Mode2Joueurs:Boolean=false;

var SVGNbJetons:int=-1;
var TourDuJoueur:int=1;


//Les sprites du Mods
var Sprite2Joueurs:Sprite=new Sprite();

var Mods_2Joueurs:TextField=CreerTextField(Sprite2Joueurs,0,0,180,25,true,false,false);
Mods_2Joueurs.htmlText="<li>Activer le mode \"2 Joueurs\"</li>";


Sprite2Joueurs.scaleX=Sprite2Joueurs.scaleY=0.4;
Mods.addChild(Sprite2Joueurs);
Sprite2Joueurs.addEventListener(MouseEvent.MOUSE_OVER,SourisIN);
Sprite2Joueurs.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);
Sprite2Joueurs.addEventListener(MouseEvent.CLICK,SwapMode);

function SwapMode(e:Event):void
{//passe du mode 1 joueur au mode 2 joueurs et vice versa
Mode2Joueurs=! Mode2Joueurs;
UpdaterBoite();
if(Mode2Joueurs)
{
Mods_2Joueurs.htmlText="<li>Désactiver le mode \"2 Joueurs\"</li>";
ShowMessage("<u>Mode 2 Joueurs</u> : Le mode 2 Joueurs se joue sur les mêmes niveaux que le mode normal. En voici les règles :<li>Chacun votre tour, il faut allumer un noeud. Avant d'allumer un noeud, vous pouvez en éteindre autant que vous le souhaitez ;</li><li>Le perdant est celui qui ne peut plus allumer de noeuds.</li>Les règles sont simples, à vous de bloquer l'adversaire !<br>Que le meilleur gagne...");
}
else
Mods_2Joueurs.htmlText="<li>Activer le mode \"2 Joueurs\"</li>";

if(contains(Fond))
removeChild(Fond);
}

function Verifier_2Joueurs():void
{//Changmeent de tours, et vérification pour savoir si la partie est terminée
var NbNoeudsPossible:int;
if(SVGNbJetons<Fond.NbJetonsUtilises)
TourDuJoueur=(TourDuJoueur+1)%2;

//Tester si gagné ou perdu :
NbNoeudsPossible=0;
for each(var Enfant:Noeud in Fond.All_Noeuds)
{//Vérifier que tous les enfants sont bien fermés
if(Enfant.Peut_Etre_Allume())
NbNoeudsPossible++;
}
if(NbNoeudsPossible==0)
Fond.Informations.htmlText="<b>Joueur "+(TourDuJoueur+1)+" a perdu !</b>";
else
Fond.Informations.htmlText="Tour de Joueur <b>"+(TourDuJoueur+1)+"</b> ("+NbNoeudsPossible+" choix)";

//Mettre à jour la sauvegarde
SVGNbJetons=Fond.NbJetonsUtilises;
}
Code source : Persos.as
//Ajout dans Mods
var SpritePerso:Sprite=new Sprite();
var Mods_Perso:TextField = CreerTextField(SpritePerso,0,0,180,20,false,false,false);
Mods_Perso.htmlText="<li>Éditeur de niveaux</li>";
SpritePerso.scaleX=SpritePerso.scaleY=0.4;
SpritePerso.y=8;
Mods.addChild(SpritePerso);

SpritePerso.addEventListener(MouseEvent.MOUSE_OVER,SourisIN);
SpritePerso.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);
SpritePerso.addEventListener(MouseEvent.CLICK,ShowLoadLevel);


//Graphismes de la boite
var LoadLevel:Sprite=new Sprite();
LoadLevel.graphics.beginFill(0x444444);
LoadLevel.graphics.lineStyle(2,0x000000);
LoadLevel.graphics.drawRect(0, 0,440,140);
LoadLevel.x=100;
LoadLevel.y=160;
LoadLevel.visible=false;
LoadLevel.alpha=.95;

var LoadLevelTexte:TextField = CreerTextField(LoadLevel,20,110,360,20,false,true);
LoadLevelTexte.type="input";
LoadLevelTexte.restrict="null,/@+-X0-9";
LoadLevelTexte.text="null,null,0/1/,2/,null,null,4/5/,3/6/";

var LoadLevelNb_Jetons:TextField = CreerTextField(LoadLevel,390,110,20,20,false,true);
LoadLevelNb_Jetons.type="input";
LoadLevelNb_Jetons.restrict="0-9";
LoadLevelNb_Jetons.text="4";

var LoadLevelCaption:TextField = CreerTextField(LoadLevel,20,10,400,100,true,true,false);

var Lancer_Level:Sprite=LevelChangeButton(0);
Lancer_Level.scaleX=Lancer_Level.scaleY=.3;
Lancer_Level.x=420;
Lancer_Level.y=90;
Lancer_Level.addEventListener(MouseEvent.CLICK,LancerChargement);
LoadLevel.addChild(Lancer_Level);

AppliquerFiltre(LoadLevel);

LoadLevelCaption.htmlText="<i>(Utilisez la roulette de la souris pour faire défiler le texte)<br>(<a href=\"http://neamar.fr/Res/AGraphe#Createur\" target=\"_blank\">http://neamar.fr/Res/AGraphe#Createur pour un tutorial plus clair</a>)<br><u>Éditeur de niveau</u><br>Un niveau est une simple chaine de caractères, les virgules délimitant des intervalles contenant un noeud chacun.<br>Un noeud sans enfant est noté null. Ainsi, pour réaliser un niveau avec un unique noeud, il faut simplement marquer <u>null</u> dans le champ de texte.<br>Pour les parents, il faut marquer le numéro d'intervalle des noeuds enfants, séparés par un / (slash). Ainsi, le premier niveau du jeu se code très simplement de la façon suivante : <u>null,null,0/1/</u> : deux enfants, et un parent qui réunit les deux.<br><b>Attention</b> : N'oubliez pas de mettre un slash à la fin de la liste !<br>Quelques astuces pour aller plus loin :<li>Vous pouvez marquer un noeud comme inextinctible en placant un X dans la liste : <u>nullX,null,0/1/X</u></li><li>Vous pouvez décaler l'affichage d'un noeud à l'aide de hook : + et -. Cela force l'affichage du noeud un cran plus haut (ou un cran moins haut). Par exemple, <u>null,null,0/1/+</u> affiche un niveau «en ligne».</li>";

addChild(LoadLevel);

function ShowLoadLevel(e:Event):void
{
LoadLevel.visible=true;
Boite.visible=false;
}


function LancerChargement(e:Event=null):void
{
LoadLevel.visible=false;
Boite.visible=false;
Fond.filters=new Array();
if(contains(Fond))
removeChild(Fond);

Fond=String2Level(LoadLevelTexte.text,"Niveau Perso",LoadLevelNb_Jetons.text);
Fond.Officiel=false;
addChild(Fond);
Fond.graphics.clear();

//Repasser la boite de message au premier plan...c'est plus pratique pour la lire :-)
setChildIndex(Fond,1);
addEventListener(Event.ENTER_FRAME,TesterFinNiveau);

//Mettre à jour la preview
LVL_Edit=String2Level(LoadLevelTexte.text,"Édition Actuelle",LoadLevelNb_Jetons.text);
LVL_Edit.Officiel=false;
// Trace("Nb :"+SolveLevel(Fond));
}

Classes

Code source : Niveau.as
//Un niveau standard : contient des liens et des noeuds !
package
{
import flash.display.Sprite;
import flash.geom.Point;
import flash.display.Shape;
import flash.events.*;
import flash.text.TextField;

public class Niveau extends Sprite
{
public var All_Noeuds:Array;//Contient l'arborescence sous forme de noeuds graphiques
public var NbJetonsUtilises:int;
public var NbJetonsToleres:int;
public var NumeroNiveau:String;
public var Mode2Joueurs:Boolean;
public var Informations:TextField = new TextField();

public var Flou:int=0;//Le niveau de flou du Fond actuel
public const FLOU_FINAL:int=32;//Le niveau maximal de Flou, une constante.
public var FlouFinal:int=FLOU_FINAL;//Le niveau final de flou demandé pour Fond.



public var Officiel:Boolean=true;
private var JetonsUtilises:Array;
private var Layer:Sprite;
private var GrandPere:Noeud;
private var Niveau_Chiffre:Array;


public function Niveau(NumeroNiveau:String,Niveau:Array,Hook:Array,Non_Extinctible:Array,Allumes:Array, DejaAllumes:Array, NbJetonsToleres:int, Hauteur:int,Largeur:int,Mode2Joueurs:Boolean)
{
this.All_Noeuds = new Array();
this.NbJetonsToleres=NbJetonsToleres;
this.JetonsUtilises=new Array();
this.NumeroNiveau=NumeroNiveau;
this.Mode2Joueurs=Mode2Joueurs;
this.Niveau_Chiffre=Niveau;
var Enfants:Array;
var i:int;

All_Noeuds=new Array(Niveau.length);
for(i=0;i<Niveau.length;i++)
{
if(Niveau[i]!=null)
{
Enfants=new Array();
for each(var Enfant:int in Niveau[i])
{
Enfants.push(All_Noeuds[Enfant]);
}
All_Noeuds[i]=AjoutNoeud(Enfants)
}
else
All_Noeuds[i]=AjoutNoeud(null);
}

//Les options supplémentaires:
//Hook
//Inextinguible
i=0;
for each(var Child:Noeud in All_Noeuds)
{
if(Hook[i]==null)
Hook[i]=0;
if(Non_Extinctible[i]==null)
Non_Extinctible[i]=false;
Child.addHook(Hook[i]);
Child.Is_Not_Eteignable(Non_Extinctible[i]);

if(Allumes[i]==true)
Child.ChangeState(true,false);
if(DejaAllumes[i]==true)
Child.aEteFerme=true;
i++;
}

//Nombre de jetons utilisables
for(i=0;i<NbJetonsToleres;i++)
{
JetonsUtilises[i]=new Noeud(new Array(),this);
if(!Mode2Joueurs)
this.addChild(JetonsUtilises[i]);
JetonsUtilises[i].x=Largeur-(i+1)*25;
JetonsUtilises[i].y=15;
JetonsUtilises[i].scaleX=JetonsUtilises[i].scaleY=.5;
}

Informations.x = 500;
Informations.y = 0;
Informations.width=150;
Informations.height=20;
Informations.background = true;
Informations.border=true;
Informations.selectable=false;
if(Mode2Joueurs)
addChild(Informations);

this.graphics.beginFill(0xFFFFFF);
this.graphics.drawRect(0, 0, Largeur, Hauteur);
GrandPere=this.All_Noeuds[All_Noeuds.length-1];
UpdateNbJetons();//Si des noeuds sont allumés dès le départ, les compter

}

public function CreerArbreParents(NoeudActuel:Noeud):void
{
for each(var Child:Noeud in NoeudActuel.Enfants)
{//Smells like recursivity again ;-)
Child.addParent(NoeudActuel);
CreerArbreParents(Child);
}
}

public function UpdateNbJetons():void
{
var Comparaison:Boolean;
var Nb:int=0;
for each(var Child:Noeud in All_Noeuds)
{
if(Child.Ferme==true)
Nb++;
}
this.NbJetonsUtilises=Nb;
for(var i:int=0;i<this.NbJetonsToleres;i++)
{
Comparaison=(i<this.NbJetonsUtilises);
JetonsUtilises[i].ChangeState(Comparaison,false);//Ne pas mettre à jour, sinon on a une réference infinie et stack overflow
if(Comparaison)
JetonsUtilises[i].scaleX=JetonsUtilises[i].scaleY=.7;
else
JetonsUtilises[i].scaleX=JetonsUtilises[i].scaleY=.5;
}
}

public function Niveau2String():String
{//Renvoyer le niveau actuel sous la forme d'un string
var Level:String="";
for(var i:int=0;i<Niveau_Chiffre.length;i++)
{
var Child:Array=Niveau_Chiffre[i];
if(Child==null)
Level +="null";
else
{
for each(var BiChild:int in Child)
{
Level +=BiChild.toString()+"/";
}
}
if(this.All_Noeuds[i].Ferme==true)
Level+="@";
if(this.All_Noeuds[i].Peut_Eteint==false)
Level+="X";
if(this.All_Noeuds[i].Hook_Y==-1)
Level +="-";
if(this.All_Noeuds[i].Hook_Y==1)
Level +="+";
if(this.All_Noeuds[i].aEteFerme==true)
Level +="!";
Level +=",";
}
Level=Level.slice(0,Level.length-1);
return Level;
}

public function Nettoyer():void
{
//Nettoyer le niveau
GrandPere.DeleteYourself();
All_Noeuds=new Array();
//Il y a un bug qui empêche la suppression des liens, donc on les vire manuellement.
while(this.numChildren > 0)
{
this.removeChildAt(0);
}
}

private function AjoutNoeud(Enfants:Array=null):Noeud
{//Ajoute un noeud en mémoire, et retourne son occurence
if(Enfants==null)
Enfants=new Array();
var NouveauNoeud:Noeud=new Noeud(Enfants,this);
this.addChild(NouveauNoeud);
NouveauNoeud.x=NouveauNoeud.y=-20
return NouveauNoeud
}
}
}
Code source : Noeud.as
//Un noeud quelconque
package
{
import flash.display.Sprite;
import flash.geom.Point;
import flash.events.*;
import flash.filters.BitmapFilter;
import flash.filters.GlowFilter;

public class Noeud extends Sprite
{
public var Enfants:Array;//La liste des enfants
public var Parents:Array;//idem pour les parents
public var Ferme:Boolean;//Si le noeud est allumé, Ferme vaut true
public var Hook_Y:int;
public var Peut_Eteint:Boolean;//Les noeuds avec une croix à l'intérieur ont Peut_Eteint=false
public var aEteFerme:Boolean;//Le noeud a déjà été allumé

private const RAYON:int=15;
private var Layer:Niveau;//Le sprite conteneur
private var Arc_Liste:Array=new Array();//La liste des liens vers les enfants
private var Final_Glow:int;//Le rayonnement du noeud
private var Current_Glow:Number;//La valeur de rayonnement désirée
private var NbPlacements:int;//Le nombre de fois que le noeud a été placé
private const PI:Number=3.141;//...

public function Noeud(Enfants:Array,Layer:Niveau)
{//Instancie un nouveau noeud
this.Enfants = Enfants;
this.Parents=new Array();
this.Ferme=false;
this.aEteFerme=false;
this.Layer=Layer;
this.Final_Glow=this.Current_Glow=0;
this.NbPlacements=0; //Le nombre de fois que le noeud a été déplacé dans l'arbre, cf. fonction GererXY.
this.Hook_Y=0;
this.Peut_Eteint=true;

this.graphics.beginFill(0xEEEEEE);
this.graphics.lineStyle(1,0x000000);
this.graphics.drawCircle(0, 0, RAYON);

this.addEventListener(MouseEvent.MOUSE_MOVE,SourisIN);
this.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);
this.addEventListener(MouseEvent.CLICK,SourisClick);

RecreerArbreLien();
}

public function RecreerArbreLien():void
{//Recrée l'ensemble des liens
//D'abord les supprimer proprement
RemoveLiens();
var Lien:Arc;
//Et refaire l'ensemble
for each(var Child:Noeud in Enfants)
{
Lien=new Arc(new Point(this.x,this.y),new Point(Child.x,Child.y));
Arc_Liste.push(Lien);
Layer.addChild(Lien);
Layer.setChildIndex(Lien,0);
//Ne pas oublier d'informer l'enfant que l'on a refait l'arbre :
Child.RecreerArbreLien();
}
}

public function Profondeur():int
{//Renvoie la profondeur de l'objet, i.e le nombre maximal de sous enfants disponibles.
//Utilise la récursivité en faisant appel à ses enfants.
var Max:uint=0;
if(this.Enfants.length==0)
return 1;
else
{
for each(var Child:Noeud in Enfants)
{
Max=Math.max(Max,Child.Profondeur());
}
return 1+Max;
}
}

public function addParent(Parent:Noeud):void
{//Ajoute un parent au noeud
this.Parents.push(Parent);
}

public function AddFocus(Level:int):void
{//Ajoute un flou à la forme.
for each(var Enfant:Noeud in Enfants)
{//Demande aux enfants d'afficher un flou aussi, mais un peu plus faible
Enfant.AddFocus(Math.max(0,Level-1));
}

//Puis lancer l'animation
this.Final_Glow=Level;
this.addEventListener(Event.ENTER_FRAME,ChangeFiltre);
}

public function addHook(Level:int):void
{
this.Hook_Y=Level;
}

public function Is_Not_Eteignable(Valeur:Boolean):void
{
this.Peut_Eteint=! Valeur;
if(Valeur==true)
{
this.graphics.moveTo(-RAYON*Math.sin(PI/4),-RAYON*Math.sin(PI/4));
this.graphics.lineTo(RAYON*Math.sin(PI/4),RAYON*Math.sin(PI/4));
this.graphics.moveTo(-RAYON*Math.sin(PI/4),RAYON*Math.sin(PI/4));
this.graphics.lineTo(RAYON*Math.sin(PI/4),-RAYON*Math.sin(PI/4));
}
}

public function GererXY(NiveauActuel:int,NiveauMax:int,Largeur:int,Position:int):void
{//Cette fonction a pour but de gérer l'agencement des noeuds dans le graphe.
//Le noeud recoit des ordres : sa position y est determinée par son imbrication
//Sa position X est determinée par la variable Position.
//Largeur lui indique que tous ses enfants devront être dans un rectangle de Position-Largeur/2 à Position+Largeur/2.
var Child:Noeud; //Pour les boucles!
var FutureLargeur:Number=Largeur/this.Enfants.length;
var i:int=0;
var YDemande:int=20+400*(NiveauActuel-1)/(NiveauMax-1);//Le niveau Y des enfants
var DecalageYHook:int=400/(NiveauMax-1);//Une valeur de décalage de Y
var PositionInitialeXEnfants:int=Position-Largeur/2;

this.NbPlacements++;//Nombre de fois que l'on place le noeud. Si on le place plusieurs fois, il faut le centrer de façon à ce qu'il soit bien, et pour cela on a besoin d ela moyenne, donc du nombre de déplacements à prendre en compte.
this.x=(Position+(this.NbPlacements-1)*this.x)/this.NbPlacements; //Si il a déjà été placé auparavant, on le centre : un simple calcul de moyenne avec pondération.
this.y = Math.max(this.y,YDemande) + this.Hook_Y*DecalageYHook;//Sile noeud a déjà été placé, ne conserver que le y le plus élevé. Ajouter le Hook si besoin.

for each(Child in this.Enfants)
{//Smells like recursivity again ;-)
i++;
Child.GererXY(NiveauActuel+1,NiveauMax,FutureLargeur,PositionInitialeXEnfants + (i-.5)*FutureLargeur);
}
}

public function Peut_Etre_Allume():Boolean
{//Ce noeud est il allumable ?
var unEnfantOuvert:Boolean=false;
for each(var Enfant:Noeud in Enfants)
{//Vérifier que tous les enfants sont bien fermés
if(!Enfant.Ferme)
unEnfantOuvert=true;
}
return (!unEnfantOuvert && !this.aEteFerme && !this.Ferme); //Si le noeud a déjà été fermé, il ne peut pas être refermé.
}

public function Peut_Etre_Eteint():Boolean
{//Ce noeud est-il éteignable ?
return (this.Ferme && this.Peut_Eteint);
}

public function ChangeState(Etat:Boolean,TriggerUpdate:Boolean=true):void
{//Passer d'un état à un autre.
this.Ferme=Etat;
if(Etat)
{
this.addEventListener(Event.ENTER_FRAME,ChangeFiltre)
if(!this.Peut_Eteint)
this.addEventListener(Event.ENTER_FRAME,Tourne);
}
else
{
this.aEteFerme=true;
this.filters=new Array();
}

//On peut demander à ne pas mettre à jour le niveau. Pratique dans le cas où on change juste les jetons indiquants le nombre de noeuds allumés :)
if(TriggerUpdate)
Layer.UpdateNbJetons();//Demander une MAJ du nombre de jetons
}

public function DeleteYourself():void
{//Demande l'auto destruction du noeud et de ses enfants.
RemoveLiens();
for each(var Enfant:Noeud in Enfants)
{//Demander la destruction des enfants.
Enfant.DeleteYourself();
}
//Puis supprimer du niveau le noeud
if(Layer.contains(this))
Layer.removeChild(this);
}


//Fonctions privées
private function SourisIN(e:MouseEvent):void
{//Ajoute un filtre indiquant que la forme est hoverée
AddFocus(Profondeur());
}
private function SourisOUT(e:MouseEvent):void
{//Ajoute le filtre indiquant que la forme n'est plus hoverée
AddFocus(0);
}

private function SourisClick(e:Event):void
{//swapper l'état de ouvert à fermé ou vice versa.
if(Peut_Etre_Eteint())
this.ChangeState(false);//Eteindre le noeud
else if(Peut_Etre_Allume())
this.ChangeState(true);//L'allumer
}

private function Tourne(e:Event):void
{//Fait tourner le noeud, modulo 360.
this.rotation=(this.rotation+1)%360;
}

private function ChangeFiltre(e:Event=null):void
{//gestion des filtres dans le temps

//Filtre HOVER
if(Current_Glow<Final_Glow)
Current_Glow=Math.min(Final_Glow,Current_Glow+0.2);
else
Current_Glow=Math.max(Final_Glow,Current_Glow-0.1);

var ListeFiltres:Array = new Array(new GlowFilter(0xFF0000, 0.8,16,16,Current_Glow,1,false,false));

//Filtre FORME FERMEE
if(Ferme)
{
if(Peut_Eteint)
ListeFiltres.push(new GlowFilter(0x00FF00, 1,16,16,2*Profondeur(),1,false,false));
else
ListeFiltres.push(new GlowFilter(0xFEA900, 1,16,16,2*Profondeur(),1,false,false));
}

//Applications des filtres
for each(var Lien:Arc in Arc_Liste)
Lien.filters=ListeFiltres;

this.filters=ListeFiltres;

if(Current_Glow==Final_Glow)
this.removeEventListener(Event.ENTER_FRAME,ChangeFiltre);
}

private function RemoveLiens():void
{//Supprime les images des liens entres noeuds.
var Lien:Arc;
for each(Lien in Arc_Liste)
{
if(Layer.contains(Lien))
Layer.removeChild(Lien);
}
Arc_Liste=new Array();
}
}
}
Code source : Arc.as
//Un lien (arete, arc...peut importe le nom !)
//Rien de bien compliqué, si ce n'est la définition de la courbe de bézier lors de l'utilisation de curveTo.
package
{
import flash.display.Shape;//Shape consomme moins de ressource que Sprite. En contrepartie, il ne peut pasz avoir d'enfants, et ne recoit pas d'évenements souris. Ca tombe bien, on en a pas besoin :)
import flash.geom.Point;

public class Arc extends Shape
{
public var Parent:Point;
public var Enfant:Point;

public function Arc(Parent:Point,Enfant:Point)
{//Instancie un nouveau lien : prend en paramètre le parent et l'enfant (en fait, leurs coordonnées)
this.Parent=Parent;
this.Enfant=Enfant;
this.Refresh();
}

public function ChangeOrigine(Parent:Point):void
{//Change l'origine du lien
this.Parent=Parent;
this.Refresh();
}

private function Refresh():void
{//Redessine entièrement le lien. Cette méthode est utilisée lorsque l'un des noeuds se déplace||est déplacé
this.graphics.clear();
this.graphics.moveTo(this.Parent.x,this.Parent.y);
this.graphics.lineStyle(2,0x444444);
// this.graphics.lineTo(this.Enfant.x,this.Enfant.y);
this.graphics.curveTo(this.Enfant.x,this.Parent.y + (this.Enfant.y-this.Parent.y)/2, this.Enfant.x,this.Enfant.y);//Rien de bien méchant : on commence à dessiner sur le parent, puis on définit un point de controle à mi-chemin, et on va jusqu'à Enfant.
}
}
}

Bonus

En bonus, le fichier de résolution qui donne le nombre minimal de jetons à utiliser pour un niveau.
Attention, il n'est pas optimisé, et est particulièrement long à l'éxécution...nécessitera une fonction trace.

Code source : Resolution.as
function SolveLevel(Level:Niveau,MaxJetons:int=0,NbActions:int=0):int
{//Prend en paramètre un niveau, renvoie le nombre minimum de noeuds à allumer simultanément.
//Utilise la récursivité...
var Enfant:Noeud;//Variable utilisée dans les boucles
var Jetons_actuel:int; //Le nombre de jetons consommés au maximum pendant la résolution
var BestValue:int; //Le meilleur Jetons_actuel que l'on ait trouvé;
var HasMadeChange:Boolean=false;//Si il vaut false à la fin de l'étape 1, on arrete tout
var DernierParentTraite:Noeud=new Noeud(new Array(),null);

//D'abord, présenter quelques fonctions utiles :
function Parents_Allumes(Element:Noeud, index:int, Tableau:Array):Boolean
{//renvoie Vrai si l'élement a été fermé, ou est fermé.
return (Element.Ferme || Element.aEteFerme)
}

//Etape 0 : Si GrandPere est allumé, arrêter la recherche !
if(Level.All_Noeuds[Level.All_Noeuds.length-1].Ferme==true)
return 0;

//Etape 1 : Eteindre tous les noeuds qui n'ont plus besoin d'être allumés
for each(Enfant in Level.All_Noeuds)
{
if(Enfant.Ferme==true && Enfant.Parents.every(Parents_Allumes))
{//Every effectue un test sur tous les parents : si ils ont tous été allumés, on peut éteindre ce noeud.
Enfant.ChangeState(false);//Eteindre l'item
}
}

//Etape 2 : Allumer tous les noeuds possibles, les passer à la fonction SolveLevel.
//Récuperer le plus petit résultat, et le renvoyer.
Jetons_actuel=1000;
BestValue=1000;
for each(Enfant in Level.All_Noeuds)
{
if(Enfant.Peut_Etre_Allume() && DernierParentTraite != Enfant.Parents[0])
{//L'allumer
Enfant.ChangeState(true);
Jetons_actuel=Math.max(Level.NbJetonsUtilises,MaxJetons);
Jetons_actuel=Math.max(Jetons_actuel,SolveLevel(String2Level(Level.Niveau2String(),"Résolution"),Jetons_actuel,NbActions + 1)); //On est obligé de passer par un export en string, car sinon on agirait sur la copie originale...ce qui n'est pas idéal.
//SolveLevel va nous renvoyer le nombre de jetons requis. On ne le garde que si il est intéressant ;)
Enfant.ChangeState(false);
Enfant.aEteFerme=false;
HasMadeChange=true;
DernierParentTraite=Enfant.Parents[0];
}
BestValue=Math.min(Jetons_actuel,BestValue);
}
if(!HasMadeChange)
return 10000;//Renvoyer une grosse valeur !

return BestValue;
}
Auteur
Neamar
Date
Aout 2008
But
Finir le jeu
Voir aussi
Compiler l'AS3
Retour
Retour à AGraphe
Menu
Index des ressources

Chargement du sommaire...