http://prototyprally.com/a-steroids4k/
@author Martin Jonasson (grapefrukt@grapefrukt.com)
Licensed under the MIT License
Copyright (c) 2009 Martin Jonasson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// forked from grapefrukt's a-steroids4k
/**
* http://prototyprally.com/a-steroids4k/
* @author Martin Jonasson (grapefrukt@grapefrukt.com)
*/
/*
Licensed under the MIT License
Copyright (c) 2009 Martin Jonasson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
http://prototyprally.com/a-steroids4k/
*/
package {
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.filters.GlowFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.utils.getTimer;
/**
* a-steroids4k a game by grapefrukt (grapefrukt@grapefrukt.com)
* http://prototyprally.com/a-steroids4k/
*/
[ SWF( backgroundColor = '#2c2c2c', width = '500', height = '500' ) ]
public class M extends Sprite {
private var dc :Sprite;
private var ship :S;
public var txt :TextField;
private var points :int;
public var r :Boolean = false;
public var st :int = -3000;
private var c :Vector.<D>;
public function M():void {
dc = new Sprite;
c = new Vector.<D>;
addChild(dc);
ship = new S;
ship.die();
addChild(ship);
txt = new TextField();
txt.textColor = 0xdddddd;
txt.selectable = false;
txt.defaultTextFormat = new TextFormat("_sans", 10);
txt.text = "a-steroids4k\nby grapefrukt\nclick to start";
addChild(txt);
stage.addEventListener("mouseDown", hm);
stage.addEventListener("mouseUp", hm);
addEventListener("enterFrame", ef);
stage.quality = "medium";
}
private function hm(e:*):void {
if(!r && getTimer() - st > 3000){
for each(var d:D in c) d.alive = false;
for (var i:uint = 0; i < 16; i++) {
da();
}
ship.die();
r = true;
st = getTimer();
}
ship.laser = 0;
if(e.buttonDown && ship.alive && r) ship.laser = 350;
ship.laser ? ship.lgfx.filters = [new GlowFilter(0xffffff, .6, 12, 12)] : ship.lgfx.filters = [];
}
private function ef(e:*):void {
var d:D;
var d2:D;
var subresult:Vector.<D>;
var grav:Point = new Point;
var grav_l:Number;
txt.x += (ship.x - txt.x + 15) * 0.1;
txt.y += (ship.y - txt.y - 15) * 0.1;
for each (d in c) {
d.u();
if (!d.alive) dcr(d);
if (r && ship.alive && d.c == 0x5cec33) {
grav.x = ship.x - d.x;
grav.y = ship.y - d.y;
grav_l = grav.length;
if (grav_l < 100) {
grav.normalize(1 - grav_l / 100);
d.vx += grav.x * 0.1;
d.vy += grav.y * 0.1;
}
if (grav_l < 10) {
d.alive = false;
txt.alpha = 1;
txt.text = ++points + " / 300";
txt.textColor = d.c; // using the color from the destructable to save a literal specification
if (points > 300) { // this ends the game
r = false;
points = 0;
txt.textColor = 0xdddddd;
txt.text = "done!\n" + ((getTimer() - st) / 1000).toFixed(1) + "s";
st = getTimer();
}
}
}
if (d.touched) {
subresult = d.s();
if (subresult == null) {
d.blit();
d.touched = false;
} else {
for each(d2 in subresult) {
d2.touched = false;
dca(d2);
}
dcr(d);
}
}
}
if (r) {
if(txt.alpha) txt.alpha -= 0.01;
ship.u();
}
if (ship.alive) {
var target:D;
var coords:Point;
var p:Point = new Point(mouseX - ship.x, mouseY - ship.y);
if(ship.laser) ship.laser = p.length * 3 + 1;
p.normalize(1);
ship.vx += p.x * .05;
ship.vy += p.y * .05;
if (ship.laser && r) {
ship.vx -= p.x * .15;
ship.vy -= p.y * .15;
var steps:uint = 0;
while (steps++ < ship.laser *.5) {
target = dcght(ship.x + p.x * steps, ship.y + p.y * steps);
if (target && target.valid) {
coords = target.ghc(ship.x + p.x * steps, ship.y + p.y * steps);
if (target.gp(coords.x, coords.y)) break;
}
}
if (target && coords) {
ship.laser = steps * 2;
if(Math.random() < .5) da(1, ship.x + p.x * steps, ship.y + p.y * steps);
target.sp(coords.x, coords.y, false);
}
}
target = dcght(ship.x, ship.y);
if (target && r) {
coords = target.ghc(ship.x, ship.y);
if (coords && target.valid) ship.die();
}
} else {
ship.x = mouseX - 5;
ship.y = mouseY - 5;
}
}
private function da(size:Number = 0, x:Number = 0, y:Number = 0):void {
var d:D = new D(size || 3 + 40 * Math.random(), size || 3 + 40 * Math.random());
d.x = x || 500 * Math.random();
d.y = y || 500 * Math.random();
dca(d);
}
public function dca(d:D):void { // destructable collection, add
c.push(d);
dc.addChild(d);
}
public function dcr(d:D):void { // destructable collection, remove
dc.removeChild(d);
var i:int = 0;
for each (var di:D in c) {
if (di == d) {
c.splice(i, 1);
break;
}
i++;
}
}
public function dcght(x:uint, y:uint):D { // destructable collection, get hit target
var result:Array = dc.getObjectsUnderPoint(new Point(x, y));
return result[0];
}
}
}
import flash.display.Sprite;
class GO extends Sprite { // GameObject
public var vx:Number = 0;
public var vy:Number = 0;
public var vr:Number = 0;
public var alive:Boolean = true;
public var age:int = 150;
public var bm:uint = 50;
public function u():void {
x += vx;
y += vy;
rotation += vr;
if (width < 8 && height < 8) {
age--;
vx *= 0.99;
vy *= 0.99;
}
if (age < 0) alive = false;
if (x > 500 + bm) {
x = -bm;
} else if (x < -bm) {
x = 500 + bm;
}
if (y > 500 + bm) {
y = -bm;
} else if (y < -bm) {
y = 500 + bm;
}
}
}
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Point;
import flash.utils.getTimer;
class S extends GO {
public var laser :Number = 0;
private var deathp :uint = 0; // death time penalty
public var lgfx :Sprite;
public function S() {
bm = 0;
graphics.beginFill(0xff0000); //NORMAL_COLOR
graphics.drawRect( -2, -2, 4, 4);
lgfx = new Sprite;
lgfx.graphics.beginFill(0x79daff); // DRILL_COLOR
lgfx.graphics.drawRect( 0, -2.5, 100, 5);
lgfx.blendMode = "add";
lgfx.scaleX = 0;
addChild(lgfx);
}
public function die():void {
alive = false;
deathp = 100; // 3s penalty at 30fps
alpha = .5;
}
override public function u():void {
super.u();
vx *= 0.99;
vy *= 0.99;
rotation = Math.atan2(vy, vx) * 57; // approximation of 180 / Math.PI, saves 16 bytes a piece
lgfx.rotation = Math.atan2(mouseY, mouseX) * 57; // approximation of 180 / Math.PI, saves 16 bytes
lgfx.scaleX = laser / 200;
lgfx.scaleY = Math.sin(getTimer()/50)*0.5 + .75;
age = 100;
if (deathp && M(parent).r) {
M(parent).txt.alpha = 1;
M(parent).txt.textColor = 0xff0000;
M(parent).txt.text = "spawn in " + deathp;
alpha = Math.sin(deathp--);
}
if (deathp == 1) {
alive = true;
deathp = 0;
alpha = 1;
M(parent).txt.text = "";
}
}
}
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.filters.GlowFilter;
class D extends GO {
public var bmp :BitmapData;
public var com :Point; // centerOfMass
public var touched :Boolean = true;
public var c :uint = 0xeeeeee;
public static const PX :uint = 3; // pixel size
public static const AC :uint = 0xff0000; // active color
public static const IC :uint = 0x000000; // inactive color
public static const RC :uint = 0xffff00; // replace color
public function D(w:int, h:int) {
bmp = new BitmapData(w, h, false, AC);
com = new Point;
filters = [new GlowFilter(0, 1, 2, 2)];
if (w * h == 1 && Math.random() < .5) c = 0x5cec33;
age += Math.random() * 50;
vx = Math.random() - .5;
vy = Math.random() - .5;
vr = Math.random() - .5;
}
public function sp(x:uint, y:uint, value:Boolean):void { // set pixel
if (x >= 0 && x < bmp.width && y >= 0 && y < bmp.height) {
bmp.setPixel(x, y, value ? AC : IC);
touched = true;
}
}
public function gp(x:uint, y:uint):uint { // get pixel
var result:uint;
if (x >= 0 && x < bmp.width && y >= 0 && y < bmp.height) result = bmp.getPixel(x, y);
return result;
}
public function blit():void {
graphics.clear();
for (var iy:uint = 0; iy < bmp.height; iy++) {
var ix:uint = 0;
var w:uint = 0;
while(ix < bmp.width){
while (bmp.getPixel(ix + w, iy) && ix + w < bmp.width) w++;
if (w == 0) {
ix++;
} else {
graphics.beginFill(c);
graphics.drawRect(ix * PX - com.x, iy * PX - com.y, w * PX, PX * 1.1);
graphics.endFill();
ix += w;
w = 0;
}
}
}
}
public function cpb(bmpd:BitmapData):BitmapData { // copy bitmap
var pixelcount :uint = bmp.width * bmp.height;
var ix :uint = 0;
var iy :uint = 0;
for (var i:uint = 0; i < pixelcount; i++) {
ix = i % bmp.width;
iy = i / bmp.width;
bmp.setPixel(ix, iy, bmpd.getPixel(ix, iy) == RC ? AC : IC); // this is horrible, but it saves 6 bytes!
}
um();
blit();
return bmp;
}
/**
* Gets the hit coordinates for a given point, the coords sent in
* should be in the stage's coordinate space
* @param x
* @param y
* @return the coordinates of the hit voxel
*/
public function ghc(x:uint, y:uint):Point {
var hitpos:Point = globalToLocal(new Point(x, y));
hitpos.x = (hitpos.x + com.x) / PX;
hitpos.y = (hitpos.y + com.y) / PX;
return hitpos;
}
public function s():Vector.<D> { // s
var ix:uint = 0;
var iy:uint = 0;
var rect:Rectangle;
var rect2:Rectangle;
var parts:Vector.<D> = new Vector.<D>;
if (alive) {
for (iy = 0; iy < bmp.height; iy++) {
for (ix = 0; ix < bmp.width; ix++) {
if (bmp.getPixel(ix, iy) == AC) {
//trace("found set pixel at:", ix, iy);
//trace("image is:", bmp.width, bmp.height);
bmp.floodFill(ix, iy, RC);
rect = bmp.getColorBoundsRect(0xffffff, RC, true);
// checks if the currently colored segment is the only one in this destructable
rect2 = bmp.getColorBoundsRect(0xffffff, AC, true);
// if it is, we skip the entire sting
if (rect2.width == 0 && rect2.height == 0 && parts.length == 0) {
// color the bitmap back to keep it until next time
bmp.floodFill(ix, iy, AC);
// only had one segment, escape the loop
return null;
} else {
var nd:D = _s(rect, ix, iy);
if (nd) parts.push(nd);
ix = rect2.x;
iy = rect2.y;
}
}
}
}
}
return parts;
}
private function _s(rect:Rectangle, firstX:uint, firstY:uint):D {
//trace("sting region at", rect);
if (!(rect.width + rect.height)) return null;
var nd:D = new D(rect.width, rect.height);
var pos:Point = localToGlobal(new Point(rect.x * PX - com.x, rect.y * PX - com.y));
nd.x = pos.x;
nd.y = pos.y;
nd.rotation = rotation;
nd.vx = vx * (.9 + Math.random() * .2);
nd.vy = vy * (.9 + Math.random() * .2);
nd.vr = vr * (.9 + Math.random() * .2);
var bmpData:BitmapData = new BitmapData(rect.width, rect.height);
bmpData.copyPixels(bmp, rect, new Point);
nd.cpb(bmpData);
bmp.floodFill(firstX, firstY, IC);
return nd;
}
private function um():void { // update mass
var points:uint = 0;
var pixelcount :uint = bmp.width * bmp.height;
var ix :uint = 0;
var iy :uint = 0;
for (var i:uint = 0; i < pixelcount; i++) {
ix = i % bmp.width;
iy = i / bmp.width;
if (bmp.getPixel(ix, iy) == AC) {
com.x += ix;
com.y += iy;
points++;
}
}
com.x = com.x / points * PX + PX / 2;
com.y = com.y / points * PX + PX / 2;
x = localToGlobal(com).x;
y = localToGlobal(com).y;
}
public function get valid():Boolean {
return bmp.width > 1 && bmp.height > 1;
}
}