Un (autre) jeu de graphe

Code de BGraphe

Licence

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

À propos du code

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

Structure de l'application

Diagramme de BGraphe

Classe principale B-Graphe

Fichiers de bases :

Code source : Bgraphe.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/B-graphe/graphe.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.Event;//Les évenements "de bases", classe dont dérive MouseEvent
import flash.events.MouseEvent;//Interaction utilisateur//souris
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. Peut être du texte au format HTML.

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;//Effet de biseau, appliqué sur le "Plus de jeux".

import flash.net.URLRequest;//Prépare une requete pour une page web.
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.sendToURL;//Execute une requete sans récuperer la réponse du serveur. Assez pratique, l'air de rien.

import flash.net.SharedObject;//Sauvegarde locale sur le PC de l'utilisateur : enregistrement du dernier niveau joué, et des niveaux débloqués.

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

public class Bgraphe extends Sprite
{
public var Fond:Niveau=new Niveau(new Array());//Fond est un Sprite instance de Niveau, qui contient le niveau en cours.

public const VERSION:String="RC1";//La version
public var Img_Fond:Loader = new Loader();//L'image en fond du player.

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 Images_Niveaux:Array=new Array();

function Bgraphe():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, si elles existent.
SAVE_LOCAL = SharedObject.getLocal("BGraphe");
SESSION = SAVE_LOCAL.data;

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

if(SESSION.NumeroNiveauActuel==null)
SESSION.NumeroNiveauActuel=SESSION.NumeroNiveauUnlockes=0;//Création de la sauvegarde.

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

include "../Trace.as";
Trace("NON FINALISEE");
Trace("VERSION EN DEVELOPPEMENT");
include "ChangementsNiveaux.as";//Pendant le niveau, teste si il est fini. Prépare le passage à un nouveau niveau, et l'affiche.
include "ChargementNiveaux.as";//La liste des niveaux, et les fonctions pour les charger en mémoire.
include "Message.as";//La boite de message. Montre un message via la méthode ShowMessage(message:String)
include "Persos.as";//Gère l'éditeur de niveau. Code assez simple.

//Enregistrer le nouveau joueur :
sendToURL(new URLRequest("http://neamar.fr/Res/BGraphe/Player.php"));//Permet de garder des statistiques sur le nombre de joueurs même quand le jeu est hébergé sur un autre site.

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


Fond.scaleX=0;//Animation
LancerNouveauNiveau();//charge le niveau de numéro NumeroNiveauActuel.

////////////////////////////////
//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/BGraphe/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
{//No comment !
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é

if(Fond.Termine)
{
if(Fond.Officiel)
TerminerNiveau(1);
else
TerminerNiveau(0);//S'il ne s'agit pas d'un niveau officiel, afficher la boite de dialogue.
}
}

/////////////////////////////////////
//Fonctions utilisées lors des changements de niveaux
/////////////////////////////////////
function TerminerNiveau(Valeur:int):void
{//Termine un niveau en affichant la boite de changement
//Débloque un nouveau niveau si le dernier est réussi.
removeEventListener(Event.ENTER_FRAME,TesterFinNiveau);

Fond.Taille=0;//Réduire
Boite.visible=true;

NumeroNiveauActuel+=Valeur;
if(NumeroNiveauActuel>NumeroNiveauUnlockes && NumeroNiveauActuel<=11)
{//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é !";
LancerNouveauNiveau();
}
else if(NumeroNiveauUnlockes==11 && NumeroNiveauActuel>=11)
{
ShowMessage("<u>Félicitations !</u> Vous avez fini les 12 niveaux proposés par le jeu.<br>Maintenant, pourquoi ne pas tenter votre chance sur le prédecesseur de ce jeu, <a href=\"http://www.neamar.fr/Res/Agraphe/\" target=\"_blank\">AGraphe</a> (même graphique, mais concept différent) ? Sinon, vous pouvez aussi éteindre votre ordinateur et reprendre une activité normale...<font size=\"-4\">(z'avez pas du boulot à faire ?)</font><br>À la revoyure, Neamar.");
NumeroNiveauActuel=11;
if(contains(Fond))
{//C'est la première fois que le jeu est fini :
removeChild(Fond);
Boite.visible=false;
}
else
UpdaterBoite();
}
else //Sinon, 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))
removeChild(Fond);//Si Fond a déjà été utilisé, le nettoyer.

Fond = GetLevelData(NumNiveau);
Fond.graphics.clear();//Supprimer le fond blanc
Fond.x=FlashWidth/2;
Fond.y=FlashHeight/2;
Fond.scaleX=Fond.scaleY=Fond.TailleActuelle=0;
addChild(Fond);
//Repasser la boite de message au premier plan
setChildIndex(Fond,1);


Fond.Taille=1;//Rezoomer
addEventListener(MouseEvent.MOUSE_WHEEL,Zoom);//Le zoom à la souris, disposé sur le stage. (sinon, il faut avoir la souris SUR le niveau I.e sur un noeud ou une arete...ce qui n'est pas pratique)

Boite.visible=false;
addEventListener(Event.ENTER_FRAME,TesterFinNiveau);//Remettre le test de fin de niveau
ChangeLevelInfo.htmlText="<u>Liste des niveaux</u>";
}
Code source : ChargementNiveaux.as
/////////////////////////////////////
//Fonctions de création de niveau
/////////////////////////////////////

function GetLevelData(Numero:int,Not_Mute:Boolean=true):Niveau
{

//La structure d'enregistrement de niveau est loin d'être optimale : avec du recul, je m'apercois que j'aurais du lui préferer quelque chose de similaire à :
// 1:2,3,4
// 2:3,4
// J'y aurais gagné en clarté et en concision !
var Message:String="";
var Liens:Array=new Array();//La representation interne du niveau
function T(...Liste):Array
{//T comme Tableau
//Plus court à écrire que new Array() à chaque fois
return Liste;
}
if(Numero==0)
{//Insolemment easy
Liens=new Array(T(0,1),T(0,2),T(0,3),T(1,2),T(1,3),T(2,3));
Message ="<u>Règles de B-graphe : <font size=\"-2\"><i>(partie 1/1)</i></font></u><br>Enlevez tout croisement entre les traits pour passer au niveau suivant.<br>Pour cela, déplacez les ronds à l'aide de la souris.";
}
else if(Numero==1)//Très facile (deux figures distinctes)
Liens=new Array(T(0,1),T(1,5),T(5,4),T(4,0),T(6,7),T(7,3),T(3,2),T(2,6));
else if(Numero==2)//Très facile
Liens=new Array(T(1,4),T(1,2),T(1,5),T(2,3),T(2,4),T(3,4),T(3,5),T(4,5),T(5,0),T(2,5),T(0,4));
else if(Numero==3)//Plutot facile
Liens=new Array(T(1,2),T(1,3),T(1,4),T(1,5),T(2,6),T(3,6),T(4,7),T(5,7),T(5,9),T(6,8),T(7,0),T(8,0),T(9,0),T(0,1));
else if(Numero==4)//Facile
Liens=new Array(T(0,1),T(0,8),T(0,9),T(1,2),T(1,3),T(2,3),T(2,4),T(2,5),T(3,7),T(4,5),T(4,6),T(5,6),T(6,7),T(7,9),T(7,11),T(8,9),T(9,10),T(10,11));
else if(Numero==5)//Moyen
Liens=new Array(T(0,1),T(0,4),T(0,5),T(1,2),T(1,5),T(2,3),T(2,6),T(3,6),T(3,7),T(4,5),T(4,9),T(5,10),T(5,8),T(5,6),T(6,8),T(6,7),T(6,11),T(7,12),T(8,10),T(8,11),T(9,10),T(9,13),T(10,13),T(10,14),T(10,11),T(11,12),T(11,15),T(12,16),T(13,14),T(14,15),T(15,16),T(11,16));
else if(Numero==6)//Moyen Dur
{
Liens=new Array(T(0,1),T(0,2),T(0,7),T(0,9),T(0,10),T(1,2),T(1,3),T(1,6),T(1,7),T(2,3),T(2,9),T(3,4),T(3,9),T(3,6),T(4,5),T(4,6),T(4,7),T(4,13),T(4,14),T(4,10),T(5,6),T(5,14),T(5,15),T(5,12),T(5,11),T(6,7),T(6,15),T(6,16),T(7,10),T(7,11),T(7,12),T(7,13),T(7,16),T(7,8),T(9,10),T(11,12),T(11,15),T(11,16),T(12,13),T(12,14),T(13,14),T(15,16));
Message ="<u>Astuce</u><br>Vous pouvez utiliser la roulette de la souris pour dézoomer le niveau, afin d'y voir plus clair. Attention cependant, seul un faible niveau de recul est permis !";
}
else if(Numero==7)//Dur
Liens=new Array(T(0,1),T(0,2),T(0,3),T(0,4),T(0,5),T(0,6),T(0,7),T(1,8),T(2,3),T(2,8),T(3,4),T(3,8),T(3,9),T(4,9),T(4,5),T(4,10),T(5,11),T(5,6),T(5,10),T(6,11),T(7,11),T(8,15),T(8,16),T(9,16),T(9,17),T(9,10),T(10,17),T(10,18),T(11,18),T(11,19),T(12,17), T(13,17),T(14,17),T(15,20),T(16,17),T(16,23),T(17,18),T(18,23),T(19,25),T(19,26),T(20,27),T(20,28),T(21,28),T(21,22),T(22,28),T(22,23),T(23,28),T(23,24),T(24,28),T(24,25),T(25,28),T(26,28),T(26,29),T(27,28),T(27,32),T(28,30),T(28,31),T(28,29),T(29,32),T(28,29),T(28,32));
else if(Numero==8)//Dur, trois figures distinctes.
Liens=new Array(T(0,1),T(0,13),T(0,8),T(0,11),T(1,2),T(1,11),T(2,3),T(2,11),T(2,12),T(3,10),T(3,4),T(4,10),T(4,5),T(5,10),T(5,6),T(6,9),T(6,7),T(6,12),T(7,9),T(7,8),T(8,9),T(8,21),T(9,21),T(9,12),T(10,12),T(11,12),T(11,13),T(12,13),T(12,14),T(12,15),T(12,16),T(12,17), T(12,18),T(12,19),T(12,20),T(12,21),T(13,14),T(13,15),T(13,17),T(13,21),T(14,15),T(15,16),T(15,17),T(16,17),T(17,18),T(17,19),T(17,21),T(18,19),T(19,20),T(19,21),T(20,21),T(30,31),T(30,37),T(30,41),T(30,42),T(30,35),T(30,36),T(31,32),T(31,33),T(31,34),T(31,41),T(31,42),T(32,33),T(32,37),T(32,39),T(32,40),T(32,41),T(33,34),T(34,42),T(34,35),T(35,36),T(36,37),T(36,38),T(37,39),T(37,41),T(37,38),T(38,39),T(39,40),T(22,23),T(22,24),T(22,25),T(22,26),T(22,27),T(22,28),T(22,29),T(23,25),T(26,28),T(23,28),T(23,24),T(24,25),T(25,29),T(29,26),T(26,27),T(27,28));
else if(Numero==9)//Inspiré par http://pigale.sourceforge.net/images/graphT0.png
Liens=new Array(T(1,2),T(1,6),T(1,8),T(1,9),T(1,10),T(1,13),T(1,16),T(1,21),T(2,6),T(2,9),T(2,14),T(2,18),T(2,22),T(2,23),T(3,4),T(3,5),T(3,8),T(4,5),T(4,7),T(5,6),T(6,0),T(6,7),T(6,8),T(6,15),T(6,18),T(6,16),T(7,5),T(7,8),T(7,9),T(7,12),T(7,18),T(7,19),T(7,23),T(7,24),T(7,26),T(7,0),T(8,5),T(8,9),T(8,10),T(8,11),T(8,12),T(8,15),T(8,16),T(8,17),T(8,25), T(9,11),T(9,12),T(9,13),T(9,14),T(9,19),T(9,20),T(10,11),T(10,21),T(11,21),T(12,24),T(12,25),T(13,14),T(13,20),T(14,20),T(15,16),T(15,17),T(16,17),T(18,26),T(18,0),T(19,22),T(19,23),T(22,23),T(24,25),T(26,0));
else if(Numero==10)//Pas facile, proche du maximum de densité théorique
Liens=new Array(T(0,1),T(0,2),T(0,5),T(0,6),T(0,8),T(0,13),T(0,16),T(1,2),T(1,3),T(1,4),T(1,5),T(2,3),T(2,11),T(2,12),T(2,13),T(3,4),T(3,11),T(4,5),T(4,11),T(4,17),T(5,6),T(5,7),T(5,17),T(6,7),T(6,8),T(7,8),T(7,9),T(7,10),T(7,17),T(8,9),T(8,10),T(8,16),T(9,10),T(9,15),T(9,16),T(9,17),T(11,12),T(11,17),T(11,18),T(11,21),T(11,27),T(12,13),T(12,14),T(12,16),T(12,18),T(13,16),T(14,15), T(14,16),T(15,16),T(15,17),T(17,18),T(18,19),T(18,21),T(18,22),T(18,23),T(18,24),T(18,26),T(18,27),T(19,20),T(19,22),T(19,23),T(20,21),T(20,22),T(20,23),T(21,22),T(21,23),T(23,24),T(23,25),T(23,26),T(24,26),T(25,26),T(25,27));
else if(Numero==11)//Le graphe maximal...à 42 noeuds. Afin de le simplifier dans sa representation, on commence par les noeuds avec le moins d'aretes. De cette façon, on obtient bien trois aretes pour CHAQUE noeud, sauf sur les trois derniers : l'anté pénultième n'en a que deux, l'avant dernier un et le dernier n'a aucune arete propre. On a donc un total de 3*n -6 aretes, ce qui est bien la limite théorique pour un graphe planaire !
{
Liens=new Array(T(42,5),T(42,3),T(42,14),T(41,12),T(41,2),T(41,1),T(40,11),T(40,6),T(40,1),T(39,1),T(39,6),T(39,12),T(38,2),T(38,6),T(38,12),T(37,2),T(37,6),T(37,10),T(36,2),T(36,3),T(36,10),T(35,3),T(35,6),T(35,10),T(34,3),T(34,6),T(34,11),T(33,1),T(33,3),T(33,11),T(32,1),T(32,3),T(32,14),T(31,1),T(31,5),T(31,14),T(30,1),T(30,5),T(30,15),T(29,0),T(29,1),T(29,15),T(28,0),T(28,5),T(28,15),T(27,0),T(27,5),T(27,13),T(26,3),T(26,5),T(26,13),T(25,0),T(25,3),T(25,13),T(24,0),T(24,3),T(24,8),T(23,0),T(23,4),T(23,8),T(22,3),T(22,4),T(22,8),T(21,3),T(21,4),T(21,9),T(20,2),T(20,3),T(20,9),T(19,2),T(19,4),T(19,9),T(18,2),T(18,4),T(18,7),T(17,0),T(17,4),T(17,7),T(16,0),T(16,2),T(16,7),T(15,0),T(15,1),T(15,5),T(14,1),T(14,3),T(14,5),T(13,0),T(13,3),T(13,5),T(12,1),T(12,2),T(12,6),T(11,1),T(11,3),T(11,6),T(10,2),T(10,3),T(10,6),T(9,2),T(9,3),T(9,4),T(8,0),T(8,3),T(8,4),T(7,0),T(7,2),T(7,4),T(6,1),T(6,2),T(6,3),T(5,0),T(5,1),T(5,3),T(4,0),T(4,2),T(4,3),T(3,0),T(3,1),T(3,2),T(2,0),T(2,1),T(1,0));
Message="Bienvenue dans le dernier niveau de BGraphe.<br>Ce niveau est spécial : tous les autres niveaux admettaient des solutions multiples. Cette fois, il s'agit du «graphe maximal mathématique» : le nombre d'arêtes est poussé à son maximum. En conséquent, une seule solution existe <font size=\"-4\">enfin...avec des arêtes droites</font>...pour vous aider, vous pouvez vous renseigner sur le «tapis de triangle» de Sierpinski. Bon courage...";
}

if(Message!="" && Not_Mute)
ShowMessage(Message);

return LoadNiveau(Liens,Numero);
}

function ChargementNiveauDirect(e:Event):void
{//Triggeré quand clic sur une des miniatures dans Boite.
while(Boite.numChildren > 0)
Boite.removeChildAt(0);//libérer de la mémoire (même si on ne peut s'assurer du passage du garbage collector)

Boite.addChild(ChangeLevelInfo);

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


function LoadNiveau(Liens:Array,Numero:int):Niveau
{//La fonction principale, qui charge le niveau dans un sprite
var Layer:Niveau; //Layer est le Sprite qui doit contenir le niveau
Layer=new Niveau(Liens,FlashWidth,FlashHeight);
Layer.NumeroNiveau=Numero;
return Layer;
}

function String2Level(Niv_String:String):Niveau
{//prend un string en paramètre, le transforme et renvoie un niveau.
var Niv_Array:Array=Niv_String.split("|");
var Liens:Array=new Array();
for each(var Lien:String in Niv_Array)
{
var Composants:Array=Lien.split(",");
Liens.push(Composants);
}
var LeNiveau:Niveau = LoadNiveau(Liens,100);
if(3*LeNiveau.All_Noeuds.length -6<LeNiveau.All_Liens.length)
ShowMessage("Ce niveau est <i>à priori</i> irréalisable !");
return LeNiveau;
}
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);

var SpriteQualiteL:Sprite=new Sprite();
var Mods_QualiteL:TextField = CreerTextField(SpriteQualiteL,0,0,180,20,false,false,false);
Mods_QualiteL.htmlText="<li>Réduire la qualité</li>";
SpriteQualiteL.scaleX=SpriteQualiteL.scaleY=0.4;
SpriteQualiteL.y=0;
Mods.addChild(SpriteQualiteL);
SpriteQualiteL.addEventListener(MouseEvent.MOUSE_OVER,SourisIN);
SpriteQualiteL.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);
SpriteQualiteL.addEventListener(MouseEvent.CLICK,ToggleQuality);


//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
{
var i:int=0;
var ThumbShot:Niveau;

function Afficher(Image:Niveau,Empl:int):void
{
Boite.addChild(Image);
Image.scaleX=Image.scaleY=0.07;
Image.x=15 + (Empl%4 +.5)*Ecart;//Le niveau est centré, il faut ajouter le paramètre .5 pour obtenir le bon emplacement
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) +.5)*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;
if(Images_Niveaux.length<NumeroNiveauUnlockes)
{
Images_Niveaux=new Array();
for(i=0;i<=NumeroNiveauUnlockes;i++)
{
ThumbShot=GetLevelData(i,true);//true indique qu'il ne faut pas afficher les messages;
ThumbShot.Passif=true;
Images_Niveaux.push(ThumbShot);
}
}
for(i=0;i<=NumeroNiveauUnlockes;i++)
{
ThumbShot=Images_Niveaux[i];
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);
LVL_Edit.y -=4;
}
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));
}

//Retour
Boite.addChild(Annuler_Choix);
Annuler_Choix.y=16;
Annuler_Choix.x=16;
Boite.addChild(ChangeLevelInfo);

//Informations
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
{
if(e.currentTarget.Officiel==true)
ChangeLevelInfo.htmlText="Charger le niveau <b>"+e.currentTarget.NumeroNiveau +"</b>";
else
ChangeLevelInfo.htmlText="Niveau Perso";
if(e.currentTarget.NumeroNiveau==NumeroNiveauUnlockes)
ChangeLevelInfo.htmlText="Dernier niveau débloqué : <b>"+e.currentTarget.NumeroNiveau +"</b>";
else if(e.currentTarget.NumeroNiveau==NumeroNiveauActuel)
{
ChangeLevelInfo.htmlText="Dernier niveau tenté : <b>"+e.currentTarget.NumeroNiveau +"</b>";
AppliquerFiltre(e.currentTarget,0x7F7FB3)
}
ChangeLevelInfo.htmlText += "<font size=\"-7\">{" + e.currentTarget.Param() + "}</font>";
}
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/BGraphe/\" target=\"_blank\">http://neamar.fr/Res/BGraphe (click to go)</a></li>");
}

function Annuler(e:Event):void
{
Fond.Taille = 1;//Rezoomer
Boite.visible=false;
addEventListener(Event.ENTER_FRAME,TesterFinNiveau);
}

function ToggleQuality(e:Event):void
{
if(stage.quality == "HIGH")
{
stage.quality="LOW";
Mods_QualiteL.htmlText="<li>Augmenter la qualité</li>";
}
else
{
stage.quality="HIGH";
Mods_QualiteL.htmlText="<li>Réduire la qualité</li>";
}
}

function Zoom(evt:MouseEvent):void
{
Fond.startDrag();
Fond.stopDrag();
if(evt.delta>0)
Fond.Taille=Math.min(Fond.scaleX + 0.2*Fond.scaleX,1.2);
else
Fond.Taille=Math.max(Fond.scaleX - 0.2*Fond.scaleX,0.2);
}

Fichiers de mod

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=",|0-9";
LoadLevelTexte.text="0,1|1,5|5,4|4,0|6,7|7,3|3,2|2,6";

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><u>Éditeur de niveau</u><br>Un niveau est une simple chaine de caractères.<br>Chaque noeud est affecté d'un numéro. Pour créer un lien marquer Noeud1,Noeud2 : par exemple <b>0,1</b>.<br>Séparez les asociations de noeuds par un pipe (la barre verticale : <b>|</b> (alt-gr + 6)).<br>Exemple : le texte chargé actuellement permet de rejouer le premier niveau.<br>Dernière information : avant chaque lancement, les noeuds sont placés de façon aléatoire sur la surface de jeu : le même niveau peut donc produire plusieurs situations initiales.";

addChild(LoadLevel);

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


function LancerChargement(e:Event=null):void
{//Lance le chargement à partir d'un niveau perso.
LoadLevel.visible=false;
Boite.visible=false;
Fond.filters=new Array();
if(contains(Fond))
removeChild(Fond);

Fond=String2Level(LoadLevelTexte.text);
Fond.Officiel=false;

Fond.graphics.clear();//Supprimer le fond blanc
Fond.x=FlashWidth/2;
Fond.y=FlashHeight/2;
Fond.scaleX=Fond.scaleY=Fond.TailleActuelle=0;
addChild(Fond);
//Repasser la boite de message au premier plan
setChildIndex(Fond,1);


Fond.Taille=1;//Rezoomer

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

//Mettre à jour la preview
LVL_Edit=String2Level(LoadLevelTexte.text);
LVL_Edit.Officiel=false;
}

Classes

Code source : Niveau.as
//Un niveau standard : contient des liens et des noeuds !
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;//Interaction utilisateur//souris


public class Niveau extends Sprite
{
public var All_Noeuds:Array;
public var All_Liens:Array;

public var Officiel:Boolean=true;

public var NumeroNiveau:int=0;

public var TailleActuelle:Number=1;//Le niveau de Taille du Fond actuel
private var TailleFinale:Number=1;//Le niveau final de Taille demandé pour Fond.

private var Is_Destructing:Boolean=false;
private var DestructionTerminee:Boolean=false;

private static const RAYON_NOEUDS:int=200; //Le rayon du cercle fait par les noeuds lors du chargement du niveau

public function Niveau(Liens:Array,Largeur:int=0,Hauteur:int=0)
{
function Shuffle(a:Noeud,b:Noeud):Number
{
return -1 + Math.floor(3*Math.random());//Mélange aléatoirement l'ensemble
}
//Trouver le nombre de noeuds :
var NbNoeuds:int=0;
var Lien:Array;
var Arete:Arc

for each(Lien in Liens)
NbNoeuds=Math.max(NbNoeuds,Lien[0],Lien[1]);

All_Noeuds = new Array();
All_Liens = new Array();
NbNoeuds++;//Petite astuce trigonométrique...
for(var i:int=0;i<NbNoeuds;i++)//Remarquer qu'on fait < et pas <=, pour contourner l'astuce
{//Ajouter le noeud
All_Noeuds.push(new Noeud(this,
Math.cos(2*Math.PI*(i/NbNoeuds))*RAYON_NOEUDS,
Math.sin(2*Math.PI*(i/NbNoeuds))*RAYON_NOEUDS
,NbNoeuds));
}

All_Noeuds.sort(Shuffle);

for each(Lien in Liens)
{
Arete=new Arc(this,All_Noeuds[Lien[0]],All_Noeuds[Lien[1]]);
All_Noeuds[Lien[0]].AjouterLien(Arete);
All_Noeuds[Lien[1]].AjouterLien(Arete);
All_Liens.push(Arete);
}
for each(Arete in All_Liens)//Tous les liens sont crées, reste à updater :
Arete.UpdateCollision(false);//Paramètre forward à false pour passer en O(n) (pas de récursion)

this.graphics.beginFill(0xFFFFFF);
this.graphics.drawRect(-Largeur/2, -Hauteur/2, Largeur, Hauteur);
}

public function set Taille(value:Number):void
{
TailleFinale=value;
this.Passif=true;
this.addEventListener(Event.ENTER_FRAME,GererTaille);
}
public function get Taille():Number
{
return TailleFinale
}

public function Param():String
{
//Renvoie les paramètres du niveau : nombre de noeuds, nombre d'arretes, et Densité
return All_Noeuds.length +" noeuds, " + All_Liens.length + " liens. Degré : " + Math.round(100*this.Densite())/100;
}

public function Densite():Number
{
//Renvoie un indice de difficulté du niveau.
//Cet indice est calculé de façon empirique.
var Difficulte:Number=1.5*All_Liens.length/All_Noeuds.length + All_Noeuds.length/35 + All_Liens.length/30 + 0.2*(All_Liens.length/2.2 - All_Noeuds.length);
return Difficulte;
// //Une méthode alternative qui utilise la classification d'Euler sur la densité maximale des graphes planaires :
// 100*(1.5*All_Liens.length/All_Noeuds.length + All_Noeuds.length/35 + All_Liens.length/30 + 0.2*(All_Liens.length/2.2 - All_Noeuds.length) - .5*(3*All_Noeuds.length -6-All_Liens.length));

}

public function get Termine():Boolean
{//Renvoie true si aucune collision détectée
function EnCollision(Lien:Arc, index:int, array:Array):Boolean
{
return Lien.Collusion;
}
//Teste si le niveau est terminé.
//S'il l'est, on lance l'auto destruction. On ne renvoie le signal terminé que lorsque tout a été nettoyé.
if(!Is_Destructing)
{
var Reponse:Boolean = !this.All_Liens.some(EnCollision);
if(Reponse)
{//Le niveau est terminé, préparer l'auto destruction !
Is_Destructing=true;
this.addEventListener(Event.ENTER_FRAME,Auto_Destruction);
//Arreter le drag
this.startDrag();
this.stopDrag();
//rendre passif tous les noeuds :
this.Passif=true;
}
}
else if(DestructionTerminee)
return true;

return false;
}

public function set Passif(value:Boolean):void
{//rend le niveau passif : on ne peut pas cliquer sur les noeuds, et ils ne s'illuminent pas. Cela permet :
// -De ne pas surharger le processeur d'évenement lrosque tous les niveaux sont affichés
// -D'éviter de continuer un drag lors d'un changement de zoom (sinon, la souris sort du noeud et on ne peut plus le dédragger)
for each(var Node:Noeud in All_Noeuds)
Node.Passif=value;
}

private function Auto_Destruction(e:Event):void
{//L'auto destruction sert à quitter le niveau de façon agréable. Auparavant, dès qu'il était terminé, le nouveau apparaissait. Ce mode n'a pas convaincu les testeurs, d'où l'apparition de l'autodestruction.
if(All_Liens.length==0)
{
DestructionTerminee=true;
this.mouseEnabled=false;
this.removeEventListener(Event.ENTER_FRAME,Auto_Destruction);
}
else if(Math.random()>Math.min(All_Liens.length/10,.9))
{
var UnLien:Arc=All_Liens.pop();
this.removeChild(UnLien);
}
this.rotation++;
if(this.Taille!=1)
this.Taille=1;
}

private function GererTaille(e:Event):void
{//Zoomer // dézoomer sur le niveau
var dS:Number = (this.TailleFinale - this.scaleX)/5;
if (Math.abs(dS)>0.003)
this.scaleX = this.scaleY += dS;
else
{
this.Passif=false;//Redonner de la réactivité au niveau.
this.TailleActuelle=this.scaleX=this.scaleY=TailleFinale;
this.removeEventListener(Event.ENTER_FRAME,GererTaille);
}
}
}
}
Code source : Noeud.as
//Un noeud quelconque
package
{
import flash.display.Sprite;
import flash.geom.Point;
import flash.events.Event;
import flash.events.MouseEvent;//Interaction utilisateur//souris
import flash.filters.BitmapFilter;
import flash.filters.GlowFilter;
// import flash.utils.getTimer;


public class Noeud extends Sprite
{
private var Final_Glow:int;//La valeur de rayonnement désirée
private var Current_Glow:Number;//Le rayonnement actuel du noeud

private var Layer:Niveau;//Le conteneur
private var Liens:Array;

private static const RAYON:int=20;

private static const Filtres:Array=new Array(
new Array(),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,.5)),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,1)),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,1.5)),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,2)),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,2.5)),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,3)),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,3.5)),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,4)),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,4.5)),
new Array(new GlowFilter(0x00FF00, 0.8,16,16,5))
);//Evite d'avoir à recréer le filtre en permanence. En plus on utilise static ce qui allège la consommation de mémoire : les filtres ne sont crées qu'une fois au démarrage de l'application, pas à chaque instanciation de noeud.

private static const Filtres_Enfants:Array=new Array(
null,
new Array(new GlowFilter(0x0000FF, 0.8,16,16,.35)),
new Array(new GlowFilter(0x0000FF, 0.8,16,16,.7)),
new Array(new GlowFilter(0x0000FF, 0.8,16,16,1.05)),
new Array(new GlowFilter(0x0000FF, 0.8,16,16,1.4)),
new Array(new GlowFilter(0x0000FF, 0.8,16,16,1.75)),
new Array(new GlowFilter(0x0000FF, 0.8,16,16,2.1)),
new Array(new GlowFilter(0x0000FF, 0.8,16,16,2.45)),
new Array(new GlowFilter(0x0000FF, 0.8,16,16,2.8)),
new Array(new GlowFilter(0x0000FF, 0.8,16,16,3.15)),
new Array(new GlowFilter(0x0000FF, 0.8,16,16,3.5))
);//Même raison,

public function Noeud(Layer:Niveau,x:Number,y:Number,NbNoeuds:int=1)
{//Instancie un nouveau noeud

this.Layer = Layer;
this.Liens=new Array();

this.x=x;
this.y=y;

this.graphics.beginFill(0xEEEEEE);
this.graphics.lineStyle(1,0x000000);
this.graphics.drawCircle(0, 0, Math.max(7,RAYON-NbNoeuds));//Plus il y a de noeuds, plus le dessin est petit. Mais on impose une taille minimum !

Layer.addChild(this);
this.Final_Glow=this.Current_Glow=0;
}

public function AjouterLien(Lien:Arc):void
{
Liens.push(Lien);
}

public function set Passif(value:Boolean):void
{
if(value==true)
{
//Empeche toute action sur le noeud
this.removeEventListener(MouseEvent.MOUSE_OVER,SourisIN);
this.removeEventListener(MouseEvent.MOUSE_OUT,SourisOUT);
this.removeEventListener(MouseEvent.MOUSE_DOWN,SourisDown);
// this.Dragger();
SourisOUT(); //Simuler une sortie de souris, pour supprimer le drag et les filtres.
}
else
{//Remettre les évenements souris.
this.addEventListener(MouseEvent.MOUSE_OVER,SourisIN);
this.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);
this.addEventListener(MouseEvent.MOUSE_DOWN,SourisDown);
}
}

//Fonctions privées



private function Dragger(e:Event=null):void
{//MAJ des collisions
for each(var Lien:Arc in Liens)
Lien.Refresh();
}

private function SourisDown(e:Event):void
{//Dragger : supprimer les évenements obsolets, ajouter les nouveaux.
this.removeEventListener(MouseEvent.MOUSE_DOWN,SourisDown);
this.removeEventListener(MouseEvent.MOUSE_OUT,SourisOUT);//Quand on dragge trop vite, l'évenenement se déclenche et les filtres font du yoyo.
this.addEventListener(MouseEvent.MOUSE_UP,SourisUp);
this.addEventListener(MouseEvent.MOUSE_MOVE,Dragger);
this.startDrag();
}
private function SourisUp(e:Event):void
{
this.removeEventListener(MouseEvent.MOUSE_UP,SourisUp);
this.removeEventListener(MouseEvent.MOUSE_MOVE,Dragger);

this.addEventListener(MouseEvent.MOUSE_DOWN,SourisDown);
this.addEventListener(MouseEvent.MOUSE_OUT,SourisOUT);

this.stopDrag();
// DerniereAction=0;
this.Dragger(); //En cas de drag rapide, il peut y avoir décalage : avec cette instruction, on met à jour de façon sûre.
}

private function AddFocus(Level:int):void
{//Ajoute un flou à la forme.
this.Final_Glow=2*Level;//Il y a un facteur 2 entre ce qui est demandé et ce qui est dessiné
this.addEventListener(Event.ENTER_FRAME,ChangeFiltre);
}

private function SourisIN(e:MouseEvent):void
{//Ajoute un filtre indiquant que la forme est hoverée
AddFocus(5);
}
private function SourisOUT(e:MouseEvent=null):void
{//Ajoute un filtre indiquant que la forme n'est plus hoverée
AddFocus(0);
}

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+1);
else
Current_Glow=Math.max(Final_Glow,Current_Glow-0.5);

this.filters=Filtres[int(Current_Glow)];

for each(var Lien:Arc in Liens)
{
if(Lien.Extremites[0]==this)
Lien.Extremites[1].filters = Filtres_Enfants[int(Current_Glow)];
else
Lien.Extremites[0].filters = Filtres_Enfants[int(Current_Glow)];
}

//Enlever l'évenement afin de décharger la mémoire.
if(Current_Glow==Final_Glow)
this.removeEventListener(Event.ENTER_FRAME,ChangeFiltre);
}
}
}
Code source : Arc.as
//Un lien (arete, arc...peut importe le nom !)
package
{
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.geom.Point;
import flash.filters.BitmapFilter;
import flash.filters.GlowFilter;

import flash.geom.ColorTransform;


public class Arc extends Shape
{

private static var Defaut_Color:ColorTransform= new ColorTransform();
private static var Highlight_Color:ColorTransform = new ColorTransform(1, 1, 1,1, 255,-255,-255);

public var Extremites:Array;//Extremites contient les noeuds parents

private var Layer:Niveau;
private var All_Liens:Array;
private var All_Noeuds:Array;


//la representation graphique de l'arc :
private var Depart:Point;//Depart est l'emplacement du premier parent, avec un peu de trigonométrie...cf. plus bas
private var Arrivee:Point;//idem
private var Angle:Number;//l'angle entre les deux noeuds.

//Les informations associées :
private var CollisionAvec:Array=new Array();
private var DernierEtat:Boolean=false;
private var Forward:Boolean=false;

private const Rayon:Number=.05;

public function Arc(Layer:Niveau,Point1:Noeud,Point2:Noeud)
{//Instancie un nouveau lien : prend en paramètre le conteneur et les extremités.
this.Layer = Layer;
Layer.addChild(this);
Layer.setChildIndex(this,0);

this.Extremites=new Array(Point1,Point2);
this.All_Liens = Layer.All_Liens;//garder en mémoire qu'il ne s'agit que d'une copie par référence : la mise à jour de l'un agit sur le second et vice versa.
this.All_Noeuds= Layer.All_Noeuds;
this.Refresh();
}

public 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é
//Met aussi à jour la liste des collisions.
this.graphics.clear();
this.Angle=GetAngle();//On est obligé de ruser, sinon tous les liens d'un même noeud seraient considérés commme intersectant (ils auraient le centre du noeud en point de collision)/
//La ruse consiste à placer les points AUTOUR du centre, à "Rayon" de distance : ainsi, il n'y a plus de points communs. Le problème, c'est qu'on laisse ouvert le point central...en zoomant, on peut donc tricher. (dans certains cas, pas tout le temps !)

var Cosinus:Number=Rayon*Math.cos(this.Angle);
var Sinus:Number=Rayon*Math.sin(this.Angle);
this.Depart=new Point(
this.Extremites[0].x+Cosinus,
this.Extremites[0].y+Sinus
);

//Pour l'arrivée : en théorie :
//this.Extremites[1].x+Rayon*Math.cos(this.Angle+Math.PI),
//this.Extremites[1].y+Rayon*Math.sin(this.Angle+Math.PI) mais cos(x+pi)=-cos(pi) et sin(x+pi)=-sin(pi)
this.Arrivee=new Point(
this.Extremites[1].x-Cosinus,
this.Extremites[1].y-Sinus
);

this.graphics.moveTo(Depart.x,Depart.y);
this.graphics.lineStyle(2,0x444444);
this.graphics.lineTo(Arrivee.x,Arrivee.y);

this.UpdateCollision();
}

public function get Collusion():Boolean
{
return (CollisionAvec.length!=0);
}

public function UpdateCollision(Forward:Boolean=true):void
{//Met à jour la liste des collisions.
//Le paramètre Forward indique si les messages doivent se transmettre : par exemple, si une arete A a une collision avec B, qu'on la déplace de façon à enlever la collision, A demandera à B de mettre à jour sa liste de collisions, sauf si Forward vaut false.
this.Forward=Forward;

CollisionAvec = All_Liens.filter(TestCollisionArcArc);

// var Filtres:Array=new Array();
// var DegreCollision:int=CollisionAvec.length;
// if(DegreCollision!=0)//on pourrait aussi utiliser la méthode this.Collusion mais on cherche à faire le moins de calculs possibles, cette méthode étant souvent utilisée.
// Filtres.push(new GlowFilter(0xFF0000, 0.8,5,5,DegreCollision,1,false,false));
//
// this.filters=Filtres;
var DegreCollision:int=CollisionAvec.length;
if((DegreCollision!=0)!=DernierEtat)
{
if(DegreCollision!=0)//on pourrait aussi utiliser la méthode this.Collusion mais on cherche à faire le moins de calculs possibles, cette méthode étant souvent utilisée.
this.transform.colorTransform = Highlight_Color;
else
this.transform.colorTransform = Defaut_Color;
DernierEtat=(DegreCollision!=0);
}
}

private function TestCollisionArcArc(Lien:Arc, index:int, array:Array):Boolean
{//on effectue plusieurs tests, du moins lent au plus lent.
//(fonction appelée via Array*.filter)
var Resultat:Boolean = (Lien!=this && this.hitTestObject(Lien) && this.intersectionArc(Lien));

if(this.Forward && ((!Resultat && this.CollisionAvec.indexOf(Lien)!=-1) || Resultat))
{//Il y a une collision de moins !
//Demander la MAJ du trait, puisqu'il est peut être libre maintenant :
Lien.UpdateCollision(false);//Pas besoin de re-forwarder le message
}
return Resultat;
}

private function intersectionArc(Lien:Arc):Boolean
{//Renvoie true si une intersection existe entre les deux segments.
var A:Point=this.Depart;
var B:Point=this.Arrivee;
var C:Point=Lien.Depart;
var D:Point=Lien.Arrivee;
var r_num:Number = (A.y-C.y)*(D.x-C.x)-(A.x-C.x)*(D.y-C.y);
var r_den:Number = (B.x-A.x)*(D.y-C.y)-(B.y-A.y)*(D.x-C.x);

if (r_den==0) return false; // Pas d'interesection
var r:Number = r_num/r_den;
if (r<=0 || r>=1) return false; // Intersection en dehors du segment [AB]

var s_num:Number = (A.y-C.y)*(B.x-A.x)-(A.x-C.x)*(B.y-A.y);
var s_den:Number = (B.x-A.x)*(D.y-C.y)-(B.y-A.y)*(D.x-C.x);
// this.graphics.clear();
if (s_den==0) return false; // Pas d'interesection
var s:Number = s_num/s_den;
if (s<=0 || s>=1) return false; // Intersection en dehors du segment [CD]

//Si on est encore là, il y a intersection...
return true;
}


private function GetAngle():Number
{//Renvoie l'angle entre les extremités.
//Rien que des maths !
var x:Number=Extremites[1].x-Extremites[0].x;
var y:Number=Extremites[1].y-Extremites[0].y;

if(y!=0)
return (Math.PI*(y/Math.abs(y)))*.5 - Math.atan(x/y);
else if(x>0)
return 0;
else
return Math.PI;
}
}
}
Auteur
Neamar
Date
Aout 2008
But
Finir le jeu
Voir aussi
Compiler l'AS3
Retour
Retour à BGraphe
Menu
Index des ressources

Chargement du sommaire...