Rapidité et stratégie : CoinStack

Ce jeu flash a été développé à l'aide de FlashDevelop, un puissant IDE gratuit pour Flash, mais malheureusement non disponible sous Linux.
Le jeu a donc été développé sous un Windows XP virtualisé par VirtualBox OSE (Wine ne fonctionnant pas pour FlashDevelop, ou plutôt : Wine ne supportant pas le framework .NET2.0 nécessaire à l'utilisation de FlashDevelop).
Le développement

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

Vous pouvez générer la documentation du code via l'outil asdoc distribué par défaut avec le Flex SDK.

Classes de CoinStack

Fichiers de bases :

Code source : Main.as
?package 
{
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.net.navigateToURL;
import flash.net.sendToURL;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFieldType;
import gs.easing.Elastic;
import gs.TweenLite;

import gs.OverwriteManager;
import mochi.as3.*;

/**
* Crée un nouveau jeu CoinStack.
*
* <p>Le but de cette application Flash est de ramasser un maximum de pièces.
* Une pièce ne peut être ramassée que si elle n'est pas recouverte par d'autres pièces.
* Au niveau du code, cette application est extrêmement simple.</p>
* <p>On distingue les classes suivantes :</p>
* <ul>
* <li>Level : Un niveau. Affiche une pub, l'aide, puis le jeu. A la fin du temps réglementaire, les pièces disparaissent une à une. Lorsque la dernière pièce a disparue, le niveau envoie l'évenement Level.LEVEL_ENDED, qui est récupére par cette classe pour enregistrer le score grâce au leaderboard mochiads ;</li>
* <li>Coin : Une pièce.</li>
* </ul>
*
* <p>En plus de ces deux classes, on trouve :</p>
* <ul>
* <li>Global : une classe qui contient en statique toutes les constantes du jeu, ainsi que le score et autre ;</li>
* <li>SecondSprite : un sprite dynamic, pour afficher les publicités mochiads.</li>
* </ul>
* @author Neamar
*/
public class Main extends Sprite
{
/**
* L'image du billet de 500?
*
*/
public var Fond:Loader = new Loader();

private var Sponsor:Loader = new Loader();
private var Logo:Loader = new Loader();

private var Niveau:Level;
private var Scores:SecondSprite = new SecondSprite();

private var EnregistrerScore:Sprite = new Sprite();
private var EnregistrerScore_Texte:TextField = new TextField();
private var EnregistrerScore_Pseudo:TextField = new TextField();
private var EnregistrerScore_OK:TextField = new TextField();

public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point

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

OverwriteManager.init();

Global.stage = this.stage;

addChild(Scores);

MochiServices.connect("42bc08b63834e4b6", Scores);

Niveau = new Level();
Niveau.addEventListener(Level.LEVEL_ENDED, niveauTermine);

Fond.load(new URLRequest("http://neamar.fr/Res/CoinStack/Images/500.jpg"),new LoaderContext(true));
addChild(Fond);
setChildIndex(Fond, 0);

//On met cet écouteur d'évenement ici pour ne pas le répéter.
EnregistrerScore_Pseudo.addEventListener(KeyboardEvent.KEY_DOWN, toucheAppuyee);
EnregistrerScore_OK.addEventListener(MouseEvent.CLICK, saveScore);

Sponsor.load(new URLRequest("http://neamar.fr/Res/CoinStack/Images/MiniJeu.png"), new LoaderContext(true));
Sponsor.addEventListener(MouseEvent.CLICK, visiterSponsor);

Logo.load(new URLRequest("http://neamar.fr/Res/CoinStack/Images/CoinStack.png"), new LoaderContext(true));
Global.Logo = Logo;
}

/**
* Valide la case avec le pseudo du joueur.
*
* @param e L'évenement clavier.
* @author Neamar
*/
private function toucheAppuyee(e:KeyboardEvent):void
{
if (e.charCode == 13 || e.keyCode == 13)
EnregistrerScore_OK.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
}

/**
* Fonction appelée quand l'évenement Level.LEVEL_ENDED est émis.
*
* Affiche une boite permettant d'enregistrer son pseudo et son score, ainsi que le sponsor du jeu.
* @param e Inutile
* @author Neamar
*/
public function niveauTermine(e:Event=null):void
{
removeEventListener(Level.LEVEL_ENDED, niveauTermine);
this.stage.removeChild(Niveau);

EnregistrerScore.graphics.clear();
EnregistrerScore.addChild(EnregistrerScore_Texte);

EnregistrerScore_Texte.multiline = true;
EnregistrerScore_Texte.width = 380;
//EnregistrerScore_Texte.height = 100;
EnregistrerScore_Texte.x = 20;
EnregistrerScore_Texte.y = 5;
EnregistrerScore_Texte.htmlText ="<font size=\"20\">Fin de la partie.</font><br>Votre score : <b>" + Global.Score + "</b><br><br>Entrez votre pseudo pour enregistrer vos points :";
EnregistrerScore_Texte.selectable = false;


EnregistrerScore.addChild(EnregistrerScore_Pseudo);

EnregistrerScore_Pseudo.autoSize = TextFieldAutoSize.CENTER;
EnregistrerScore_Pseudo.type = TextFieldType.INPUT;
if(EnregistrerScore_Pseudo.text=='')
EnregistrerScore_Pseudo.text = "Pseudo";
EnregistrerScore_Pseudo.width = 100;
EnregistrerScore_Pseudo.height = 25;
EnregistrerScore_Pseudo.background = true;
EnregistrerScore_Pseudo.backgroundColor = 0xFFFFFF;
EnregistrerScore_Pseudo.alpha = .4;
EnregistrerScore_Pseudo.x = EnregistrerScore_Texte.width/2 - EnregistrerScore_Pseudo.width / 2;
EnregistrerScore_Pseudo.y = EnregistrerScore_Texte.height - 20;

addChild(EnregistrerScore);
EnregistrerScore.scaleX = EnregistrerScore.scaleY = 1.4;
EnregistrerScore.x = Global.FLASH_WIDTH / 2 - 200;
EnregistrerScore.y = 480;
TweenLite.to(EnregistrerScore,2,{y:35,ease:Elastic.easeOut});
EnregistrerScore.width = 400;
//EnregistrerScore.height = 200;
EnregistrerScore.filters=new Array(Global.SHADOW);

EnregistrerScore.graphics.lineStyle(2, 0, .8);
EnregistrerScore.graphics.beginFill(0xFFFFFF, .4);
EnregistrerScore.graphics.drawRoundRect(0, 0, EnregistrerScore.width, 120, 10, 10);

EnregistrerScore.addChild(EnregistrerScore_OK);
EnregistrerScore_OK.text = "Enregistrer";
EnregistrerScore_OK.selectable = false;
EnregistrerScore_OK.textColor = 0xB30000;
EnregistrerScore_OK.x = 300;
EnregistrerScore_OK.y = 80;

//Le sponsor MiniJeu gratuit :
addChild(Sponsor);
Sponsor.x = Global.FLASH_WIDTH / 2 - 303 / 2;//303 = Sponsor.width
Sponsor.y = 220;


Global.stage.focus = EnregistrerScore_Pseudo;
EnregistrerScore_Pseudo.setSelection(0, EnregistrerScore_Pseudo.text.length);
}

/**
* Fonction appelée quand l'utilisateur a entré son pseudo et appuyé sur "Enregistrer".
*
* Enregistre le score et l'envoie aux serveurs de MochiAds, affiche le leaderBoard.
* @param e Inutile
* @author Neamar
*/
public function saveScore(e:Event):void
{
if (EnregistrerScore_Pseudo.text == "Pseudo")
return;

removeChild(EnregistrerScore);
removeChild(Sponsor);
var o:Object = { n: [4, 4, 6, 9, 4, 2, 12, 4, 11, 8, 11, 12, 2, 5, 15, 1], f: function (i:Number,s:String):String { if (s.length == 16) return s; return this.f(i+1,s + this.n[i].toString(16));}};
var boardID:String = o.f(0,"");
MochiScores.showLeaderboard({boardID: boardID, score: Global.Score, name: EnregistrerScore_Pseudo.text,onClose:init});
}

private function visiterSponsor(e:MouseEvent):void
{
navigateToURL(new URLRequest("http://www.mini-jeu-gratuit.fr/vip/neamar/"));
}

}
}
Code source : Level.as
?package 
{
import adobe.utils.CustomActions;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.filters.BevelFilter;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.text.TextField;
import flash.utils.Timer;

import mochi.as3.*;
import gs.TweenLite;

/**
* Un niveau.
*
* <p>Chaque niveau contient Global.COINS_IN_GAME pièces.<br/>
* On affiche d'abord une publicité pendant que les pièces tombent, puis on montre l'aide.<br/>
* Un clic quitte l'aide et lance le niveau. Une fois le temps écoulé, le spièces disparaissent une à une.
* @author Neamar
*/
public dynamic class Level extends Sprite
{
public static const LEVEL_ENDED:String = "FIN_NIVEAU";

/**
* Vaut false tant que le jeu n'a pas commencé : écite que l'on puisse cliquer sur les pièces pendant l'affichage de la pub.
*/
public var hasStarted:Boolean = false;

/**
* Les différents types de pièces que l'on peut rencontrer dans le jeu (définies dans le jeu).
*/
public static const PiecesPossibles:Array/*Class*/ = new Array(Coin.p1Cent, Coin.p2Cents, Coin.p5Cents, Coin.p10Cents, Coin.p20Cents, Coin.p50Cents, Coin.p1Euro, Coin.p2Euros);

/**
* Valeurs en points de chaque pièce.
*/
public static const PiecesValeurs:Array/*int*/= new Array(1, 2, 5, 10, 20, 50, 100, 200);

/**
* Nombre de pièces que contient actuellement le niveau. Sa valeur n'est pas mise à jour une fois que la partie a commencée.
*
* <p>Cette variable sert uniquement lors de l'apparition des pièces.</p>
*/
public var NbPieces:int = 0;



private var Chrono:Timer;
private var ChampTexte:TextField = new TextField();
private var TexteSprite:SecondSprite = new SecondSprite();
private var FondTexteSprite:Sprite = new Sprite();
private var apresMessage:Function;

/**
* Créer un nouveau niveau.
*
* @param
*/
public function Level()
{
function fin_pub(e:Event = null):void
{
TexteSprite.filters = new Array(new BevelFilter(), new GlowFilter(0xFFFFFF, .9, 32, 32, 6));
TexteSprite.buttonMode = true;
TexteSprite.scaleX = TexteSprite.scaleY = 1.5;
TexteSprite.addChild(ChampTexte);
ChampTexte.y = 150/TexteSprite.scaleX;
ChampTexte.width = Global.FLASH_WIDTH/TexteSprite.scaleX;
ChampTexte.height = Global.FLASH_HEIGHT/TexteSprite.scaleY;
ChampTexte.selectable = false;
//ChampTexte.textColor = 0;
ChampTexte.multiline = true;

afficherMessage(lancerNiveau, "<b>But du jeu</b><br>Ramassez le plus d'argent possible en cliquant sur les pièces.<br>Vous ne pouvez récupérer une pièce que si elle n'est pas recouverte par d'autres pièces.<br><br><br><b>Score</b><br>Chaque pièce vous rapporte sa valeur en points (2? : 200 points, 1 centime : 1 point).<br>Si vous tentez de ramasser une pièce recouverte, vous perdez sa valeur.<br><br><b>Fin du jeu</b><br>Le jeu se termine après " + Math.floor(Global.LEVEL_TIME / 1000) + " secondes.<br><br><b><i><font color=\"#FF0000\">Cliquez pour commencer...</font></i></b>");
}

//Mettre sur la scène le niveau.
Global.stage.addChild(this);

this.addEventListener(Event.ENTER_FRAME, creerPiece);


addChild(TexteSprite);


MochiAd.showInterLevelAd( { clip:TexteSprite, id:"42bc08b63834e4b6", res:"640x480", no_bg:true, ad_started:creerPiece, ad_finished:fin_pub } );
}

/**
* Lance le jeu une fois la pub et l'aide affichées.
*
* @param
*/
public function lancerNiveau():void
{
removeEventListener(Event.ENTER_FRAME, creerPiece);

//Créer tous les objets
for (var i:int = NbPieces; i < Global.COINS_IN_GAME; i++)
{
var NouvellePiece:Coin = creerPiece();
NouvellePiece.alpha = 0;
TweenLite.to(NouvellePiece, 3, { alpha:1 } );
}

Global.Score = 0;
hasStarted = true;
Chrono = new Timer(Global.LEVEL_TIME, 1);
Chrono.start();
Chrono.addEventListener(TimerEvent.TIMER, finNiveau);
}

private function creerPiece(e:Event=null):Coin
{
if (NbPieces <= Global.COINS_IN_GAME)
{
var Piece:Coin = new Coin(this,NbPieces % PiecesPossibles.length);
addChild(Piece);
NbPieces++;
if(this.contains(TexteSprite))
setChildIndex(TexteSprite, numChildren - 1);//Garder le texte au premier rang.

return Piece;
}
else
return null;
}

private function supprimerPiece(e:Event = null):void
{
if (numChildren!=0)
{
(getChildAt(0) as Coin).destroy();
}
else
{
removeEventListener(Event.ENTER_FRAME, supprimerPiece);
dispatchEvent(new Event(LEVEL_ENDED));
trace("Fin de la partie...");
}
}

private function finNiveau(e:TimerEvent):void
{
//afficherMessage(null, "<font size=\"20\">Fin de la partie.</font><br><br><br><b>Votre score : " + Global.Score + "</b>");

addEventListener(Event.ENTER_FRAME, supprimerPiece);
}

private function afficherMessage(apresClick:Function,Texte:String):void
{
addChild(TexteSprite);
TexteSprite.addChild(Global.Logo);
Global.Logo.scaleX = Global.Logo.scaleY = 1 / TexteSprite.scaleX;

TexteSprite.addChild(FondTexteSprite);

FondTexteSprite.graphics.clear();
FondTexteSprite.graphics.beginFill(0xFFFFFF, .3);
FondTexteSprite.graphics.drawRect(0, 0, Global.FLASH_WIDTH, Global.FLASH_HEIGHT);
FondTexteSprite.alpha = 0;
TweenLite.to(FondTexteSprite, 1, { alpha:.3 } );

ChampTexte.htmlText = Texte;
TexteSprite.addEventListener(MouseEvent.CLICK, supprimerMessage);
apresMessage = apresClick;
}

private function supprimerMessage(e:MouseEvent = null):void
{
TexteSprite.removeEventListener(MouseEvent.CLICK, supprimerMessage);
removeChild(TexteSprite);
ChampTexte.text = '';
if (TexteSprite.contains(FondTexteSprite))
TexteSprite.removeChild(FondTexteSprite);

if(apresMessage!=null)
apresMessage();
}
}

}
Code source : Coin.as
?package 
{
import flash.display.Bitmap;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;

import gs.TweenLite;

/**
* Une pièce.
* @author Neamar
*/
public class Coin extends Sprite
{
[Embed(source="../assets/1Centime.png")]
public static var p1Cent:Class;
[Embed(source="../assets/2Centimes.png")]
public static var p2Cents:Class;
[Embed(source="../assets/5Centimes.png")]
public static var p5Cents:Class;
[Embed(source="../assets/10Centimes.png")]
public static var p10Cents:Class;
[Embed(source="../assets/20Centimes.png")]
public static var p20Cents:Class;
[Embed(source="../assets/50Centimes.png")]
public static var p50Cents:Class;
[Embed(source="../assets/1Euro.png")]
public static var p1Euro:Class;
[Embed(source="../assets/2Euros.png")]
public static var p2Euros:Class;

/**
* Le rayon de la pièce.
*/
public var Rayon:int;

/**
* La valeur en points de la pièce.
*/
public var Valeur:int;

private var Parent:Level;

/**
* Crée une nouvelle pièce
*
* @param Parent Le niveau conteneur.
* @param IDClass Le type que doit avoir la pièce : 1?, 50 centimes...
*/
public function Coin(Parent:Level,IDClass:int)
{
this.Parent = Parent;

var CoinStyle:int = 0;

var Img:Bitmap;
Img = new Level.PiecesPossibles[IDClass];
this.Valeur = Level.PiecesValeurs[IDClass];
this.Rayon = Math.min(Img.width, Img.height) / 2;

Img.smoothing = true;
Img.x = -Img.width/2;
Img.y = -Img.height/2;


this.addChild(Img);
this.filters = new Array(Global.SHADOW);
this.addEventListener(MouseEvent.CLICK, supprimerPiece);

this.cacheAsBitmap = true;

this.x = Global.FLASH_WIDTH * Math.random();
this.y = Global.FLASH_HEIGHT * Math.random();
this.rotation = -180 + Math.random() * 360;
}

/**
* Détruit la pièce pour gagner de la mémoire.
*/
public function destroy():void
{
Parent.removeChild(this);
removeChild(getChildAt(0));
removeEventListener(MouseEvent.CLICK, supprimerPiece);
}

/**
* Supprime la pièce une fois qu'on lui a cliquée dessus.
*/
public function supprimerPiece(e:MouseEvent=null):void
{
if (!Parent.hasStarted)
return;

var i:int = Parent.numChildren - 1;
var estDessous:Boolean = false;
while (Parent.getChildAt(i) != this)
{
var Piece:Coin = (Parent.getChildAt(i) as Coin);
if (Math.pow(this.x - Piece.x, 2) + Math.pow(this.y - Piece.y, 2) < Math.pow(this.Rayon + Piece.Rayon - Global.TOLERANCE, 2))
{
TweenLite.to(this, .7, { scaleX:.7, scaleY:.7 } );
TweenLite.to(this, .3, { delay:.7, scaleX:1, scaleY:1 } );
TweenLite.to(Piece, 1, { rotation:Piece.rotation+180+Math.random()*180 } );
estDessous = true;
}
i--;
}
if (!estDessous)
{
Global.Score += Valeur;
destroy();
}
else
Global.Score -= Valeur;
trace(Global.Score);

}
}

}

Constantes Globales

Code source : Global.as
?package 
{
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Stage;
import flash.filters.DropShadowFilter;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;



/**
* ...
* @author Neamar
*/
public class Global
{
public static var stage:Stage;

public static const FLASH_WIDTH:int = 640;
public static const FLASH_HEIGHT:int = 480;

public static const LEVEL_TIME:int = 60*1000;
public static const COINS_IN_GAME:int = 500;
public static const TOLERANCE:int = 3;//La tolérance indique de combien de pixels peut se tromper le joueur, certaines jonctions étant assez traitres.

public static const SHADOW:DropShadowFilter = new DropShadowFilter(7, -45, 0, 0.8, 4, 4, 2);
//public static const LIGHT:DropShadowFilter = new DropShadowFilter(3, -45, 0xFFFFFF, 0.5, 2, 2, 3,1,true);

public static var Score:int = 0;

public static var Logo:Loader;

}

}

Spécial

MochiAds demande un sprite dynamique pour afficher ses scores, d'où cette classe inutile.

Code source : SecondSprite.as
?package 
{
import flash.display.Sprite;

/**
* ...
* @author Neamar
*/
public dynamic class SecondSprite extends Sprite
{
public function SecondSprite()
{

}
}

}
Auteur
Neamar
Date
Aout 2008
Voir aussi
Compiler l'AS3
Retour
Retour au jeu
Menu
Index des ressources

Chargement du sommaire...