forked from: Ghost4Koin
forked from Ghost4Koin (diff: 338)
- framerate is caped to 60, no need to put 999999999 fps - typecasting on every wildcard - bitshift for better perfs - resuse of object like point and rect - Constant value ( StageScaleMode.NO_SCALE instead of "noscale", Keyboard.UP instead of 38 etc.) - removed unnecessary final - mouseEnabled = false and mouseChildren = false on displayObject that don't need mouse - myTextField.selectable = false; accessibility stuff is not needed for games UI like score etc. - prerender of all tiles instead of building every tiles pixel per pixel on each frame. Now we cannot colorize or shift tiles at run time but performance is better.
ActionScript3 source code
/**
* Copyright YoupSolo ( http://wonderfl.net/user/YoupSolo )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/oHss
*/
package
{
import flash.display.StageQuality;
import flash.display.StageAlign;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.SampleDataEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.*;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.ui.Keyboard;
[SWF(backgroundColor="0xffffff",frameRate="60")]
public class Main extends Sprite
{
private var b:Bitmap;
public var frame:uint;
public var run:Boolean;
public var score:int;
public var part:Array = [];
public var t:int;
// cameraX, cameraY
public var camX:Number = 0;
public var camY:Number = 0;
// mapX, mapY
public var mx:int;
public var my:int;
// keyDown
public var ku:Boolean;
public var kl:Boolean;
public var kr:Boolean;
// Player variables
public var plX:Number = 8 * 16;
public var plY:Number = 2 * 16;
public var spdX:Number = 0;
public var spdY:Number = 0;
public var pow:Number;
public var dir:int = 1;
public var txt:TextField;
public var map:BitmapData;
public var s:Sprite;
public var bd:BitmapData;
public const RECT:Rectangle = new Rectangle(0, 0, 160, 100);
public const CAM_MAXX:int = RECT.width * 16; // 2560
public const CAM_MAXY:int = RECT.height * 16; // 1600
public const OFFSETX:int = 6 * 16;
public const OFFSETY:int = 6 * 16;
public const SCREENW:int = 16;
public const SCREENH:int = 12;
// done one for all
public const tilesStr:Array = ("0000000055555555aaaaaaaaffffffffffffffffaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9aa19aa14a604a60051005100004000410051005,ff700000f3055555f49aaaaa39ffffff3dffffff1efaaaaa0eaaaaaa0aaaaaaa0aaaaaaa0aaaaaaa0aaaaaaa06aa9aa106824a6001010510040000043c101005,064506458515851585108510851a851a15851585158515855065506550655065064506458515851585108510851a851a15851585158515855065506550655065,f100004f1455551449aaaa614effffb14ffffff14fbaaef14baaaae14aaaaaa14aaaaaa14aaaaaa14aaaaaa146aaaa91468aa29141055041040000103c10043c,5500045551eff24558ffff255cffff355cffffb41fff33b41fff33f41fff33f41ffffff41efff3f41ecf38f410effff41efffff41fffeff41ef28ff450041005,55500455550ef24551efff2551ffff3558ffffc45cffffc45cfcffc45cf3fff45cf3fff458f8ff34580ef3845cfffff41efffff48fffeff4cfb0efb400050005,5555555555100455518ff25558ffff451effff451fffb335cfff3334cfffb332cfefff338fcff3331ecf383210ffff2551fffb4551fff35551eff25555000455,55100555518fb05558fffb4510ffff048fffcc23cfffcc33cfffcc338fffff321efffc345cff0e355cffffb45cfffff41efffff48fffeff4cfb0efb400050005,555555555555555555500555551aa455558ff25551effb4551e3eb4551e3eb4551e3eb4551e3cb4551effb45558ff255551aa455555005555555555555555555,555555555555555555100555550aa455550ff455518ff2555183e2555183e2555183e2555183c255518ff255550ff455550aa455551005555555555555555555,5555555555555555555005555510a4555510f4555508f255550882555508825555088255550802555508f2555510f4555510a455555005555555555555555555,45100451118ae34450f00b051c00002413055084834551c38044110380441102c0055002c0000002820550c3120410c41c00002450f00a05118af34445100451,208151520002818006a2818002020080060200800802818020028181598589895528189859081818520818069028108528181006200028285528182855281818,08908000602069066028590660a069066062890660a0890608206906a69a5526555555559a656994108180a0060280800602800006028020060280a010898162").split(",");
public const tiles:Array = [];
public var point:Point = new Point();
// Sounds
public var snd:Sound;
public var sndType:int;
public function Main()
{
// config stage
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.quality = StageQuality.LOW;
stage.stageFocusRect = false;
stage.tabChildren = false;
// add custom menu
new CustomMenu(this);
// remove click
this.mouseEnabled = false;
this.mouseChildren = false;
run = false;
// build the Atlas
for (var i:int = 0 ; i < tilesStr.length; i++ )
{
tiles[i] = buildTile( i, false );
}
for (i = 0 ; i < tilesStr.length; i++ )
{
tiles[i+tilesStr.length] = buildTile( i, true );
}
map = new BitmapData(RECT.width, RECT.height, false, 0x999999);
bd = new BitmapData( (SCREENW-1) * 16, (SCREENH-1) * 16 , true, 0xff00cc00); // 240*3 = 720 / 176*3 = 528
// the display
b = new Bitmap(bd);
b.scaleX = b.scaleY = 2;
addChild(b);
// minimap
b = new Bitmap(map);
b.alpha = 2 / 3;
b.x = stage.stageWidth - b.width - 10;
b.y = 10;
addChild(b);
// location square for minimap
s = new Sprite();
s.mouseEnabled = false;
s.graphics.lineStyle(1);
s.graphics.drawRect(0, 0, 14, 7); // 15 & 11
addChild(s);
// score
txt = new TextField();
txt.defaultTextFormat = new TextFormat("Arial", 24, null, true, null,null,null,null, TextFormatAlign.CENTER );
txt.width = 300;
txt.height = 150;
txt.selectable = txt.mouseEnabled = false;
addChild(txt);
SoundMixer.soundTransform = new SoundTransform(.01);
// the sound object
snd = new Sound();
snd.addEventListener(SampleDataEvent.SAMPLE_DATA, gds);
// action listener
addEventListener(Event.ENTER_FRAME, ef);
stage.addEventListener(KeyboardEvent.KEY_DOWN, key);
stage.addEventListener(KeyboardEvent.KEY_UP, key);
}
public function start():void
{
var i:int, j:int, k:int, l:int, m:int;
txt.x = -50;
txt.y = 10;
frame = 0;
camX = 0;
camY = 0;
plX = 48;
plY = 64;
pow = 60;
spdX = 0;
spdY = 0;
dir = 1;
score = 0;
part = [];
map.fillRect(RECT, 0x999999);
// Build level
for (i = 0; i < 400; i++)
{ // Number of platforms
j = (Math.random() * map.width) >> 0; // x
k = ((Math.random() * 95) >> 0) + 5; // y (start at 5 as lowest)
l = (Math.random() * 12) >> 0; // platform length
if (i == 0) // build start platform
{
j = 2;
k = 5;
l = 5;
}
for (m = 0; m < l; m++)
{
map.setPixel(j + m, k, 0);
if (Math.random() < 0.6)
map.setPixel(j + m, k + 1, 0);
}
j = int(Math.random() * 160); // x
k = int(Math.random() * 100); // y
if (i % 3 == 0 && map.getPixel(j, k) == 0x999999) // dont place gold on top of solid ground
map.setPixel(j, k, 0xffff00); // Draw coin
}
run = true;
sndType = 1;
snd.play();
}
public function key(e:KeyboardEvent):void
{
if (e.keyCode == Keyboard.UP)
{
ku = (e.type == KeyboardEvent.KEY_DOWN);
}
if (e.keyCode == Keyboard.LEFT)
{
kl = (e.type == KeyboardEvent.KEY_DOWN);
}
if (e.keyCode == Keyboard.RIGHT)
{
kr = (e.type == KeyboardEvent.KEY_DOWN);
}
if (e.keyCode == Keyboard.ENTER)
{
if (!run)
start();
}
}
// game loop
public function ef(e:Event):void
{
var i:int, j:int, k:int, l:int, m:int, p0:int, p1:int, p2:int, p3:int, t:int, colA:int;
frame++;
var dmg:int = 0; // check if hit by mine
if (ku)
{
spdY -= 1.2;
}
if (pow <= 0 && run)
{
run = false;
sndType = 4;
snd.play();
}
if (kl)
spdX -= 1.2;
if (kr)
spdX += 1.2;
if (spdX < -9)
spdX = -9;
if (spdX > 9)
spdX = 9;
if (spdY < -9)
spdY = -9;
if (spdY > 9)
spdY = 9;
if (!ku)
spdY += 1;
if (!kl && !kr)
spdX *= 0.5;
if (Math.abs(spdY) < 0.5)
spdY = 0;
if (Math.abs(spdX) < 1)
spdX = 0;
if (!run)
{
spdX = 0;
spdY = 0;
}
// Update player position and perform collision detection
if (spdX > 0)
dir = 1;
else if (spdX < 0)
dir = -1;
plX += spdX;
plY += spdY;
// i = x front, j = x back, k = push back
i = (dir == 1) ? 15 : 0;
j = (dir == 1) ? 3 : 12;
k = (dir == 1) ? -15 : 15;
// Check if collision with coin
p0 = (plX + 8) >> 4;
p1 = (plY + 8) >> 4;
if (map.getPixel(p0, p1) == 0xffff00)
{
// Found a coin
sndType = 3;
snd.play();
map.setPixel(p0, p1, 0x999999);
//pow += 15;
score++;
for (l = 0; l < 10; l++)
{
part.push({x: p0 * 16, y: p1 * 16, sx: Math.random() * 30 - 15, sy: Math.random() * 10 - 5, col: 0xbab800, cnt: 8});
}
}
if (map.getPixel(p0, p1) == 0xff0000)
{
map.setPixel(p0, p1, 0x999999);
pow -= 15;
dmg = 1;
// Explosion
for (l = 0; l < 30; l++)
{
part.push({x: p0 << 4, y: p1 << 4, sx: Math.random() * 30 - 15, sy: Math.random() * 30 - 15, col: 0, cnt: 16});
}
sndType = 4;
snd.play();
}
// Check every corner of the sprite, if 0 then we are inside something solid
p0 = map.getPixel( int( (plX+i) >> 4 ), int( plY >> 4 ) ); // front head
p1 = map.getPixel( int( (plX+i) >> 4 ), int( (plY+15) >> 4 ) ); // front foot
p2 = map.getPixel( int( (plX+j) >> 4 ), int( plY >> 4 ) ); // back head
p3 = map.getPixel( int( (plX + j) >> 4 ), int( (plY + 15) >> 4 ) ); // back foot
if ( spdY < 0 )
{
// Jumping, check head
if ( p2 == 0 )
{
plY = (int( plY >> 4 ) << 4) + 16;
spdY = 0;
}
if ( p1 == 0 )
{
plX = (int( (plX+i) >> 4 ) << 4) + k// move back
spdX = 0;
}
if ( p0 == 0 && ( p2 != 0 && p1 != 0 ) )
{
// ouch, down or back?!?
if ( spdY >= 0 || ((plY) % 16) < 11 )
{
plX = (int( ( plX ) >> 4 ) << 4) + j// move back
spdX = 0;
}
else
{
plY = (int( (plY + 15) >> 4 )) << 4;// move down
spdY = 0;
}
}
// Jumping / pooping :)
if ( spdY < -3 )
{
part.push( { x:plX + Math.random() * 12 + 4 , y:plY + 15, sx:Math.random() * 4 - 2 + spdX * 0.5, sy:Math.random() * 4 + 1, col:0x6316128, cnt:4 } );
}
}
else
{
if ( p0 == 0 )
{
plX = (int( (plX+i) >> 4 ) << 4) + k// move back
spdX = 0;
}
if ( p3 == 0 )
{
plY = (int( (plY + 15) >> 4 ) << 4) - 16// move up
spdY = 0;
}
if ( p1 == 0 && ( p0 != 0 && p3 != 0) )
{
// ouch, up or back?!?
if ( spdY == 0 || ((plY + 15) % 16) > 8 )
{
plX = (int( ( plX ) >> 4 ) << 4) + j// move back
spdX = 0;
}
else
{
plY = (int( (plY + 15) >> 4 ) << 4) - 16// move up
spdY = 0;
}
}
}
// Update camera pos from player pos - with smoothing
camX = camX + (((plX - OFFSETX) - camX) * 0.3);
camY = camY + (((plY - OFFSETY) - camY) * 0.3);
// 2560
if (camX < 0)
camX = 0;
if (camX > CAM_MAXX)
camX = CAM_MAXX;
if (camY < 0)
camY = 0;
if (camY > CAM_MAXY)
camY = CAM_MAXY;
// Update map position
mx = camX >> 4;
my = camY >> 4;
// Position the rectangle in the mini-map
s.x = b.x + mx;
s.y = 10 + my;
txt.text = "ENERGY: " + int(pow) + "\nSCORE: " + score;
// Render bitmap
bd.lock();
var rect:Rectangle = new Rectangle(0, 0, 240, 176);
if (run)
if (!dmg)
bd.fillRect(rect, 0xffffffff);
else
bd.fillRect(rect, 0xffff0000);
else
{
bd.fillRect(rect, 0xffffffff);
txt.x = 10;
txt.y = 10;
frame--;
txt.text = " FINAL SCORE " + score + "\n\nPRESS ENTER TO START";
if (frame == 0)
txt.text = "PRESS ENTER TO START";
}
var bol:Boolean = false;
for (i = 0 ; i < SCREENH ; i++)
{
for (j = 0 ; j < SCREENW ; j++)
{
p0 = map.getPixel(mx + j, my + i);
bol = false;
if (p0 != 0x999999)
{
if (run)
colA = 0;
else
colA = 4;
p1 = map.getPixel(mx + j, my + i - 1);
if (p0 == 0xffff00) // found a gold coin
{
var coin:Array = [8, 9, 10, 10, 9];
t = coin[ (frame >> 2) % 4];
bol = ( (frame >> 2) % 4) > 2;
colA = 8;
}
else if (p0 == 0xff0000) // found a mine
{
t = 11;
colA = (frame % 2 == 0) ? 12 : 16;
}
else if (p1 == 0)
{
t = 2;
}
else
{
p2 = map.getPixel(mx + j - 1, my + i);
p3 = map.getPixel(mx + j + 1, my + i);
if (p2 == 0 && p3 == 0)
{
t = 0;
}
else if (p2 != 0 && p3 != 0)
{
t = 3;
}
else
{
if (p2)
t = 1;
else
{
t = 1;
bol = true;
}
}
}
var val:int = bol ? 14 : 0;
drawTo(j * 16 - camX % 16, i * 16 - camY % 16, tiles[t + val]);
}
}
}
// Render particles
var p:Object;
for (i = 0; i < part.length; i++)
{
p = part[i];
bd.setPixel(p.x - camX, p.y - camY, p.col);
p.x += p.sx;
p.y += p.sy;
p.sx *= 0.7;
p.sy *= 0.7;
if (p.cnt-- == 0)
part.splice(i, 1);
}
// Render player (frame depending on speed)
if (spdY == 0 && spdX == 0)
{
t = 4;
}
else
{
if (spdY < 0)
t = 7;
else if (spdY > 0)
{
t = 6;
}
else
{
t = (frame >> 2) % 4 + 5;
if (t == 8)
t = 6;
}
}
val = (dir < 0) ? 14 : 0;
drawTo(int(plX - camX), int(plY - camY), tiles[t+val]);
// Spawn objects, coins or mines
if (run)
{
if (frame % 100 == 0)
{
j = int(Math.random() * 160); // x
k = int(Math.random() * 100); // y
if (map.getPixel(j, k) == 0x999999) // dont place gold on top of solid ground
map.setPixel(j, k, 0xffff00); // Draw coin
}
if (frame > 450 && frame % 6 == 0)
{
j = int(Math.random() * 160); // x
k = int(Math.random() * 100); // y
map.setPixel(j, k, 0xff0000); // Mines go everywhere - even inside solid ground
}
}
else
{
// game title
drawTo(100, 52, tiles[12]);
drawTo(116, 52, tiles[13]);
}
bd.unlock();
}
[Inline]
final public function drawTo(x:int, y:int, bmd:BitmapData):void
{
point.x = x;
point.y = y;
bd.copyPixels( bmd, bmd.rect, point,null,null,true);
}
public function buildTile(idx:int, inv:Boolean):BitmapData
{
var bd:BitmapData = new BitmapData(16, 16, true, 0x0);
var n:int, o:int, q:int, col:int, px:int, py:int;
var ca:int;
var str:String = tilesStr[idx];
if (idx == 0 || idx == 1 || idx == 2 || idx == 3) {
// frame tuile / gris
ca = 0;
}else if (idx == 4 || idx == 5 || idx == 6 || idx == 7) {
// frames perso / noir
ca = 20;
}else if (idx == 8 || idx == 9 || idx == 10) {
// frames pieces / jaune
ca = 8;
}else if(idx == 11){
// frames bombes / rouge
ca = 16;
}else {
ca = 20;
}
var colors:Array = [0xff000000, 0xff606060, 0xffb0b0b0, 0xffffffff, 0xff000000, 0xffFEB0B1, 0xffcc0000, 0, 0xff3e4000, 0, 0xffaba000, 0xfffff568, 0xff000000, 0, 0xff000000, 0xffff0000, 0xff000000, 0, 0xffff0000, 0xff000000, 0xff000000, 0, 0xffb0b0b0, 0xffffffff];
for (n = 0; n < 128; n++)
{
for (o = 0; o < 2; o++)
{
q = (n * 2 + o);
col = colors[ca + (uint("0x" + str.charAt(n)) >> (o * 2) & 0x3)];
if (col != 0)
{
if (!inv)
{
px = q % 16;
}
else
{
px = 15 - (q % 16);
}
bd.setPixel32(px, (q >> 4), col);
}
}
}
return bd;
}
public function gds(e:SampleDataEvent):void
{
//generate dynamic sound
// where, in time, the sound begins and ends, this affects the where on the sine curve the sound will start playing.
var i:int;
var beginAt:int = 100;
var endAt:int = 5000;
var hz:Number = 44.1;
var r:Number = (e.position / hz) + beginAt;
var pitch:int = 1000;
var flt:Number = .0;
var L:int = 8001;
var f:Number;
for (i = 0; i < 4096; i++)
{
if (sndType == 1)
{
beginAt = 100;
endAt = 2000
pitch = 1000
r = (e.position / hz) + beginAt;
if (r < endAt)
{
f = (e.position / hz) + L / 2; //time elapsed + 300 to offset the sound
flt = Math.sin((Number(i) / Math.PI / (1 - (r / pitch)) / 15)) * 40000;
}
}
if (sndType == 3)
{
beginAt = 850;
endAt = 920;
hz = 44.1;
L = 1000;
pitch = 1000
r = (e.position / hz) + beginAt;
if (r < endAt)
{
f = (e.position / hz) + L / 2; //time elapsed + 300 to offset the sound
flt = Math.tan(Number(i) / Math.PI / (1 - (r / pitch)) / 80) * 40000;
}
}
if (sndType == 4)
{
beginAt = 500;
endAt = 920;
hz = 44.1;
L = 1000;
pitch = 1000;
r = (e.position / hz) + beginAt;
if (r < endAt)
{
f = (e.position / hz) + L / 2; //time elapsed + 300 to offset the sound
flt = Math.tan(Number(i) / Math.PI / (0 - (r / pitch)) / 80) * 40000;
}
}
if (r <= endAt)
{
e.data.writeFloat(flt);
}
}
}
}
}
import flash.display.Sprite;
import flash.events.ContextMenuEvent;
import flash.net.navigateToURL;
import flash.net.URLRequest;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
internal class CustomMenu
{
private const NAME:String = "4K0ins";
public function CustomMenu(ref:Sprite):void
{
var appContextMenu:ContextMenu = new ContextMenu;
appContextMenu.hideBuiltInItems();
var cmi:ContextMenuItem = new ContextMenuItem(NAME);
var credits:ContextMenuItem = new ContextMenuItem("by YopSolo");
appContextMenu.customItems.push(cmi);
appContextMenu.customItems.push(credits);
cmi.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, _onClickCollection);
credits.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, _onClickCredits);
ref.contextMenu = appContextMenu;
}
private function _onClickCollection(e:ContextMenuEvent):void
{
navigateToURL(new URLRequest('http://www.yopsolo.fr/wp/2014/10/30/4k-plateformer/'), '_blank');
}
private function _onClickCredits(e:ContextMenuEvent):void
{
navigateToURL(new URLRequest('http://www.yopsolo.fr'), '_blank');
}
}