/**
* Copyright Cheshir ( http://wonderfl.net/user/Cheshir )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/8Vs1
*/
// forked from Cheshir's Model View Controller
// i need also TileClip
// Since I use the Google translator. I apologize for the mistakes in the comments.
package {
import flash.geom.Point;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.Event;
import flash.text.TextFieldAutoSize;
import flash.text.TextField;
import flash.display.Sprite;
public class FlashTest extends Sprite {
private static var tracetext:TextField = new TextField();
private var load:MyLoader;
private var externalData:Array = ['glay_8_8.png','stone_8_8.png','dirt_8_8.png','sand_8_8.png','aloneDwarf3_16.png','jump_sphere2_8_16.png'];
private var level_a:Level;
private var level_b:Level;
private var level_c:Level;
public function FlashTest() {
// write as3 code here...
load = new MyLoader(stage);
load.addEventListener(MyLoader.ALL_LOADED, initApp);
load.loadAll(externalData);
// It is not finished ! --- October 6, 2014
tracetext.autoSize = TextFieldAutoSize.LEFT;
tracetext.y = 300;
stage.addChild(tracetext);
}
private function initApp(e:Event):void {
tracetext.textColor = 0xf0fff0;
var m:Model = new Model();
var controll:Controller = new Controller(m, getTiles(), externalData, getBitmaps()); // вообще bitmaps должны бы передаваться в view
var view:View = new View(m, controll, stage);
level_a = new Level(66,45,new Point(10,13),[ new JumpSphere(load.loaded_D[externalData[5]],new Point(31,16)) ]);
level_b = new Level(297,45,new Point(10,10),[]); // 293 is a canione
level_c = new Level(134,45,new Point(12,4),[]); // i don't know why... but it is fun if you be out of map
controll.initMap(level_a,stage); // 134 // 66
stage.addChild(tracetext); // replace after view...
}
private function getBitmaps():Array{ // bitmaps of 4 and more is items and Player
var items:Array = [];
for(var i:int=4; i<5;i++){
items.push(load.loaded_D[externalData[i]]);
}
return items;
}
private function getTiles():Array{ // bitmaps of 0 to 3 is tiles
var tilesToView:Array =[];
for(var i:int=0;i<4;i++){
tilesToView.push(new Tile(load.loaded_D[externalData[i]], 8));
}
return tilesToView;
}
private static var numLog:Number = 0;
public static function log(v:Object):void{
numLog++;
if(numLog%5 == 0){
tracetext.text = '';
}
tracetext.appendText('\n'+v.toString());
}
}
}
import flash.events.MouseEvent;
import flash.events.ProgressEvent;
import flash.display.LoaderInfo;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.system.SecurityDomain;
import flash.system.LoaderContext;
import flash.utils.Dictionary;
import flash.geom.Matrix;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.Stage;
import flash.ui.Keyboard;
import flash.geom.Point;
import flash.events.KeyboardEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.display.Sprite;
Class {
class Model extends EventDispatcher {
public var mapTile:Array = [];
public var player:Player;
public var currentLevel:Level;
private var selectPos:Point;
private var selX:int = 0;
private var selY:int = 0;
public static var tileSize:Number = 16;
public function Model() {
selectPos = new Point();
// init void map
for(var xi:int=0; xi<45; xi++){
mapTile[xi] = [];
for(var yi:int=0; yi<45; yi++){
mapTile[xi][yi] = 0;
}
}
}
public function setSelectTile(tile:*):void{ // what? Why here X and Y ?
mapTile[selX][selY] = tile;
/* hm... before i dispatch changes... I want to calculate the illuminance */
if(tile is Tile){
tile.numState = getIlluminance(selX,selY);
}
dispatchEvent(new MapEvent(MapEvent.TILE_CHANGE, tile, selX, selY));
}
public function getTile(x:int, y:int):* {
return mapTile[x][y];
}
public function generateMap(level:Level, tiles:Array, items:Array):void{
// now we can generate map
var sBit:BitmapData = new BitmapData(level.size,level.size);
sBit.perlinNoise(level.size,level.size,3,level.seed,false,false,7,true);
this.currentLevel = level;
var val:uint = 0;// = sBit.getPixel(12,12);
for(var i:int=0; i<level.size; i++) // okay...
{
for(var j:int=0; j<level.size; j++)
{
val = sBit.getPixel(i,j);
if(val > 1800000 && val < 2100000)
{
mapTile[i][j] = tiles[0];
} else if(val > 2100000 && val < 3000000 || val < 500000)
{
mapTile[i][j] = tiles[1];
} else if(val > 3000000 && val < 3500000)
{
mapTile[i][j] = tiles[2];
} else if(val > 5400000 && val < 6400000)
{
mapTile[i][j] = tiles[3];
}
}
}
player = new Player(items[0]);
player.mapPos.x = level.start.x;
player.mapPos.y = level.start.y;
// too many Events in 1 frame...
// now only 1 Event to 1 frame... perfect!
dispatchEvent(new MapEvent(MapEvent.MAP_INIT, null,level.size,level.size)); // :DDD
}
public function calculatePlayerPos():void
{ // this function calculate and change player.mapPos
player.mapPos.x = Math.floor( (player.x + tileSize / 2) / tileSize);
player.mapPos.y = Math.floor( (player.y) / tileSize);
// maybe in this case i must dispatch event? I'm too lazy...
//View.selectTile.x = player.mapPos.x * tileSize;
//View.selectTile.y = player.mapPos.y * tileSize;
}
public function haveItem():Item
{ // if player in point where has item, USE it
var itemPosition:Point;
for(var a:int = 0; a < currentLevel.items.length; a++)
{
itemPosition = (currentLevel.items[a] as Item).getMapPos();
if(itemPosition.x == player.mapPos.x && itemPosition.y == player.mapPos.y){
return currentLevel.items[a] as Item;
}
}
return null;
}
public function getIlluminance(toX:int,toY:int):uint
{ // illuminance is state of tile
return uint(Math.random()*3); // not work... random help us.
}
public function set playerX(val:uint):void{
this.player.mapPos.x = val;
dispatchEvent(new MapEvent(MapEvent.PLAYER_MOVE,null,0,0));
}
public function get playerX():uint{
return this.player.mapPos.x;
}
}
}
Class {
class View extends Sprite {
private var model:Model;
private var controller:Controller;
private var map:Bitmap;
public static var selectTile:Sprite;
private var matrix:Matrix;
private var clear:Sprite;
public function View(aModel:Model, oController:Controller,
target:Stage){
this.model = aModel;
this.controller = oController;
map = new Bitmap(new BitmapData(Model.tileSize*45, Model.tileSize*45, true, 0xff000000));
addChild(map);
target.addChild(this);
matrix = new Matrix();
matrix.scale(2,2);
clear = new Sprite();
clear.graphics.beginFill(0);
clear.graphics.drawRect(0,0,Model.tileSize,Model.tileSize);
selectTile = new Sprite();
selectTile.graphics.lineStyle(.5, 0xfffc91);
selectTile.graphics.drawRect(0,0,Model.tileSize,Model.tileSize);
addChild(selectTile);
model.addEventListener(MapEvent.TILE_CHANGE, setNewViewTile);
// model.addEventListener(MapEvent.PLAYER_MOVE, movePlayer);
model.addEventListener(MapEvent.MAP_INIT, drawAllMap);
target.addEventListener(Event.ENTER_FRAME, delegateContUpdate);
target.addEventListener(KeyboardEvent.KEY_DOWN, delegateContDown);
target.addEventListener(KeyboardEvent.KEY_UP, delegateContUp);
}
private function delegateContDown(e:KeyboardEvent):void{
controller.keyPress(e);
}
private function delegateContUp(e:KeyboardEvent):void{
//FlashTest.log('key up');
controller.keyUp(e);
}
private var viewMapP:Point = new Point();
private function delegateContUpdate(e:Event):void{
controller.update(e);
viewMapP.x = 250 - model.player.x;
viewMapP.y = 250 - model.player.y;
if(viewMapP.x < 0){
this.x = viewMapP.x;
} else {
this.x = 0;
}
if(viewMapP.x < -model.mapTile[0].length * Model.tileSize + 250){
this.x = -model.mapTile[0].length * Model.tileSize + 250;
}
if(viewMapP.y < 0){
this.y = viewMapP.y;
} else {
this.y = 0;
}
}
private var brush:Tile;
private function drawAllMap(e:MapEvent):void{
FlashTest.log('perfect!');
for(var i:int=0; i<e.newX; i++){
for(var j:int=0; j<e.newY; j++){
matrix.tx = i*Model.tileSize;
matrix.ty = j*Model.tileSize;
if(model.mapTile[i][j] is Tile){
brush = model.mapTile[i][j];
brush.numState = model.getIlluminance(i,j);
map.bitmapData.draw(brush.draw,matrix);
}
}
}
for(var a:int=0; a < model.currentLevel.items.length; a++)
{ // for each item in a current level
var item:Item = (model.currentLevel.items[a] as Item);
var position:Point = item.getMapPos();
var v:Sprite = item.getView();
v.x = position.x * Model.tileSize;
v.y = position.y * Model.tileSize;
addChild(v);
}
model.player.x = model.player.mapPos.x * Model.tileSize;
model.player.y = model.player.mapPos.y * Model.tileSize;
addChild(model.player); // place player
}
private function setNewViewTile(e:MapEvent):void{
matrix.tx = selectTile.x;
matrix.ty = selectTile.y;
// In this moment numState already calculate in Model
if(e.newTile is Tile){
map.bitmapData.draw(e.newTile.draw, matrix);
} else { // clear
map.bitmapData.draw(clear,matrix);
}
//FlashTest.log('tle state '+e.newTile.numState);
}
/*
Вообще, нужно сделать так... Controller ловит события из View, и говорит View, как отображать Player однако при этом он должен проверять
где Player находится и преобразовывать его координаты в значение Клеток... чтобы проверять столкновения...
*/
}
}
Class {
class Controller {
private var model:Model;
private var kodesToId:Array = [49,50,51,52,53]; // keys 1,2,3,4
// why keys... why id? maybe... tiles need put here?
private var tilesToView:Array;
private var items:Array;
private var spX:Number = 0;
private var spY:Number = 0;
private var speed:Number = 1.5;
private var player_is_fall:Boolean = false;
public function Controller(aModel:Model, tilesToView:Array, tilesId:Array, items:Array) {
this.model = aModel;
this.tilesToView = tilesToView;
this.items = items;
}
public function update(e:Event):void
{
model.player.y += spY;
model.player.x += spX;
model.calculatePlayerPos();
minimapView.x = model.player.mapPos.x;
minimapView.y = model.player.mapPos.y;
if(model.mapTile[model.player.mapPos.x][model.player.mapPos.y+1] == 0)
{ // hit detection must be here....
player_is_fall = true;
model.player.fall();
}
if(model.mapTile[model.player.mapPos.x+1][model.player.mapPos.y] is Tile && spX > 0)
{ // if right cell is Tile and move is right
model.player.x -= spX;
}
if(model.mapTile[model.player.mapPos.x-1][model.player.mapPos.y] is Tile && spX < 0)
{ // if left cell is Tile and move is left
model.player.x -= spX;
}
if(model.mapTile[model.player.mapPos.x][model.player.mapPos.y-1] is Tile)
{ // if top cell is Tile
model.player.y -= spY-1;
}
if(player_is_fall)
{
spY += 0.2;
if(model.mapTile[model.player.mapPos.x][model.player.mapPos.y+1] is Tile){
player_is_fall = false;
spY = 0; spX = 0;
model.player.st();
}
}
}
public function keyPress(e:KeyboardEvent):void
{
switch(e.keyCode)
{
case Keyboard.LEFT:
spX = -speed;
model.player.goL(); // Sorry for that - keyboard event is dispatched many times and i set new state each time...
break;
case Keyboard.RIGHT:
spX = speed;
model.player.goR();
break;
case Keyboard.UP:
if(!player_is_fall || model.player.double_jump){
player_is_fall = true;
spY = -speed*3;
}
break;
case Keyboard.SPACE:
var item:Item = model.haveItem();
if(item){
item.activate(model.player);
} else {
FlashTest.log("can't use item -> no item");
}
break;
}
}
public function keyUp(e:KeyboardEvent):void{
if(e.keyCode == Keyboard.LEFT || e.keyCode == Keyboard.RIGHT){
if(!player_is_fall){
model.player.st();
spX = 0;
}
}
}
private var minimapView:Sprite = new Sprite(); // it must be in View, but now I'm too lazy
public function initMap(level:Level, stageToTest:Stage):void // is incorrect... becouse logick must be in Model !
{ // my map is 45 x 45 tiles...
model.generateMap(level,tilesToView,items);
minimapView.graphics.beginFill(0xff0000);
minimapView.graphics.drawRect(0,0,1,1);
minimapView.graphics.endFill();
var sBit:BitmapData = new BitmapData(level.size,level.size);
sBit.perlinNoise(level.size,level.size,3,level.seed,false,false,7,true);
var nB:Bitmap = new Bitmap(sBit);
stageToTest.addChild(nB);
stageToTest.addChild(minimapView);
}
private function getIdTile(kode:uint):uint
{
for(var i:int=0; i<kodesToId.length; i++){
if(kodesToId[i] == kode){
return i;
}
}
return 0;
}
}
}
// help classes
Class {
class MapEvent extends Event {
public static const TILE_CHANGE:String = 'tileChange';
public static const MAP_INIT:String = 'mapInit';
public static const PLAYER_MOVE:String = 'playerMove';
public var newTile:Tile;
public var newX:int;
public var newY:int;
public function MapEvent(type:String, tile:Tile, x:int, y:int){
newTile = tile;
newX = x;
newY = y;
super(type);
}
}
}
Class {
class Level {
public var seed:Number = 0;
public var size:Number = 0;
public var start:Point;
public var items:Array;
public function Level(seed:Number, size:Number, start:Point, items:Array){
this.seed = seed;
this.size = size;
this.start = start;
this.items = items;
}
}
}
Class {
class Player extends Sprite // it would be good to use a template condition ... ()
{ // but it is too simple example, as "behavior" is defined in the controller. From class "Player" I just need to switch the animation
public var mapPos:Point = new Point();
public var double_jump:Boolean = false;
private var clip:TileClip;
public static const STATE_STAY:String = 'stay';
public static const STATE_RUN_R:String = 'runR';
public static const STATE_RUN_L:String = 'runL';
public static const STATE_FALL:String = 'fall';
private var currentState:String = STATE_STAY;
public function Player(frames:BitmapData){
clip = new TileClip(16,16,frames);
clip.width = 32;
clip.height = 32;
clip.frameDelay = [50,30];
clip.y = -16; clip.x = -8;
addChild(clip);
}
public function goR():void
{ // set state go right
if(currentState != STATE_RUN_R)
{
clip.currentState = 1; // animate go
clip.frameDelay = [6,6];
clip.scaleX = 2; // graphic positioning
clip.x = -8;
currentState = STATE_RUN_R;
// FlashTest.log('run right');
}
}
public function goL():void
{ // set state go left
if(currentState != STATE_RUN_L)
{
clip.currentState = 1;
clip.frameDelay = [6,6];
clip.x = 24;
clip.scaleX = -2;
currentState = STATE_RUN_L;
// FlashTest.log('run left');
}
}
public function st():void
{ // set state stay...
if(currentState != STATE_STAY)
{ // only 1 time set state
clip.currentState = 0;
clip.frameDelay = [50,30];
currentState = STATE_STAY;
// FlashTest.log('stay');
}
}
public function fall():void
{ // set animation fall
if(currentState != STATE_FALL)
{
clip.currentState = 2;
clip.frameDelay = [6,6];
currentState = STATE_FALL;
}
}
}
}
Class {
interface Item {
function getView():Sprite;
function getMapPos():Point;
function activate(to:Player):void;
}
}
Class {
class JumpSphere implements Item {
private var view:Sprite;
private var st:TileClip;
private var mapPos:Point;
public function JumpSphere(bit:BitmapData, mapPos:Point)
{
this.mapPos = mapPos;
view = new Sprite();
st = new TileClip(8,16,bit,2);
st.frameDelay = [8,8,8];
st.y = -16;
view.addChild(st);
}
public function getMapPos():Point
{
return mapPos;
}
public function getView():Sprite
{
return view;
}
public function activate(to:Player):void
{
to.double_jump = true;
st.currentState = 1;
FlashTest.log('multy jump enabled');
}
}
}
Class {
class MyLoader extends EventDispatcher {
public static const ALL_LOADED:String = 'allLoaded';
public var placeToLoad:String = 'http://cheshir.zabix.net/footlocker/';
public var loaded_D:Dictionary = new Dictionary();
// to correct name
private var WonderflFIX:String = 'http://swf.wonderfl.net/[[IM'; // this wonderfl add before my url...
private var adresses:Array = []; // download all that is in this array // Probably worth doing tileset...
private var loaded:int = 0; // count loaded elements
private var loadedContents:Array = []; // animate progress loading...
private var loadSprite:Sprite = new Sprite();
private var setFeedBack:View;
private var lastId:uint = 0;
private var stageLink:Stage;
public function MyLoader(stageToLoad:Stage){
stageLink = stageToLoad;
stageLink.addChild(loadSprite);
}
public function loadAll(newAdresses:Array):void{
this.adresses = newAdresses;
FlashTest.log('start load...');
var nowAdress:String = '';
var context:LoaderContext = new LoaderContext();
context.checkPolicyFile = true;
context.securityDomain = SecurityDomain.currentDomain;
for(var i:int=0; i<adresses.length; i++ ){
FlashTest.log('new adress num '+(i+1));
nowAdress = adresses[i];
var url:URLRequest = new URLRequest(placeToLoad+nowAdress);
var img:Loader = new Loader();
img.contentLoaderInfo.addEventListener(Event.COMPLETE, loadedBit);
img.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progressLoad);
loadedContents.push(img.contentLoaderInfo);
img.load(url, context);
}
}
private function progressLoad(e:ProgressEvent):void{
var percent:Number = 0;//(e.currentTarget as LoaderInfo).bytesLoaded / (e.currentTarget as LoaderInfo).bytesTotal * 100;
var lX:Number = 20;
var lY:Number = 20;
loadSprite.graphics.clear(); // clear graphitcs before draw...
for(var i:int=0; i < loadedContents.length; i++){ // draw each loaded progress
percent = (loadedContents[i] as LoaderInfo).bytesLoaded /(loadedContents[i] as LoaderInfo).bytesTotal * 100;
loadSprite.graphics.lineStyle(1,0x75D07B, 0.8);
loadSprite.graphics.drawRect(lX,lY,102,5);
loadSprite.graphics.lineStyle(0);
loadSprite.graphics.beginFill(0x9DD075,0.8);
loadSprite.graphics.drawRect(lX+1,lY+1,percent,3);
lY += 12;
}
}
private function loadedBit(e:Event):void{ //
var bitmap:Bitmap = e.target.content; // i want BitmapData
// i need only original name...
var nameOfLoaded:String = e.target.url.toString().slice(WonderflFIX.length+placeToLoad.length);
FlashTest.log('i have '+nameOfLoaded);
// i want safe loaded to dictionary use original name...
loaded_D[nameOfLoaded] = bitmap.bitmapData;
loaded++;
if(loaded == adresses.length){
dispatchEvent(new Event(MyLoader.ALL_LOADED,true)); // why not?
}
}
}
}
Class {
class Tile {
private var drawMe:Bitmap;
private var firstBitData:BitmapData;
private var stateMatrix:Matrix;
private var numStates:uint = 1;
private var currentState:uint = 0;
private var wh:Number = 0;
public function Tile(bitmapData:BitmapData, sizePix:Number) {
firstBitData = bitmapData;
stateMatrix = new Matrix();
numStates = uint(bitmapData.width/sizePix);
//FlashTest.log('new Tile numStates ='+numStates);
wh = sizePix;
var drawable:BitmapData = new BitmapData(sizePix,sizePix,true,0);
drawable.draw(bitmapData,stateMatrix);
drawMe = new Bitmap(drawable);
}
public function set numState(n:uint):void{
if(n>this.numStates){
n = 0;
}
this.currentState = n;
stateMatrix.tx = -this.wh * n; // only horizontal states // okey... mistake was here...
drawMe.bitmapData.draw(firstBitData,stateMatrix);
}
public function get numState():uint {
return this.currentState;
}
public function get draw():Bitmap{ // only read
return drawMe;
}
}
}
Class{
class TileClip extends Bitmap
{
public var demoMode:Boolean = false;
public var randomDelay:Boolean = false;
private var delays:Array = []; // yes... when i change delays -- current delay must change also to delays[0]
private var currentDelay:uint = 0;
private var widthFrame:Number;
private var heightFrame:Number;
private var bitData:BitmapData;
private var currFrame:uint = 0;
private var totalFrame:uint = 0;
private var currState:uint = 0;
private var totalState:uint = 0;
private var matrix:Matrix;
// Create TileClip from bitmap Data
public function TileClip(widthFrame:Number, heightFrame:Number, bitData:BitmapData, scale:Number = 1)
{
this.widthFrame = widthFrame;
this.heightFrame = heightFrame;
this.bitData = bitData; // Safe data to draw
matrix = new Matrix(); // Init Matrix transform
totalFrame = uint(bitData.width / widthFrame); // Calculate frames
totalState = uint(bitData.height / heightFrame); // Calculate states
this.bitmapData = new BitmapData(widthFrame, heightFrame, true, 0); // This is 'wiew window' of my clip
super();
currentFrame = 0;
this.width = widthFrame * scale;
this.height = heightFrame * scale;
play();
}
public function set frameDelay(val:Array):void
{
delays = val;
currentDelay = delays[0];
}
public function set currentFrame(num:uint):void
{
if (num < totalFrame) // Elementary update as looped video "currentFrame+=1"
{
this.currFrame = num;
} else {
this.currFrame = 0;
if(demoMode){currentState++;}
}
setCurrentFrame();
}
public function get currentFrame():uint
{
return this.currFrame;
}
public function set currentState(num:uint):void
{
if (num < totalState)
{
this.currState = num;
} else {
this.currState = 0;
}
setCurrentFrame();
}
public function get currentState():uint
{
return this.currState;
}
public function play():void
{
this.addEventListener(Event.ENTER_FRAME, update);
}
public function stop():void
{
this.removeEventListener(Event.ENTER_FRAME, update);
}
public function update(e:Event):void
{
//currentFrame++; // Maybe i must add delays to each frame?
/* How can I add delays to frames... Delay can be a good idea becouse my sheep slightly twitch.
I like a meditative thing ... */
if(currentDelay <= 0){
currentFrame++;
currentDelay = (randomDelay) ? uint(delays[currentFrame]*Math.random()) : uint(delays[currentFrame]);
// I like random and it make my animation not so repetitive...
// FlashTest.myConsole.clear();
// FlashTest.myConsole.log('next frame delay '+currentDelay);
} else {
currentDelay--;
}
}
private function setCurrentFrame():void // DRY (don't repeat yourself)
{
matrix.tx = -this.currFrame * widthFrame; // positioning data
matrix.ty = -this.currState * heightFrame;
this.bitmapData.fillRect(this.bitmapData.rect, 0); // clear 'wiew window'
this.bitmapData.draw(bitData, matrix);
}
}
}