;
destPoint = new Point();
sourceRect = new Rectangle();
currentPeak = 0;
lastPeak = 0;
timer = new Timer(45);
timer.addEventListener(TimerEvent.TIMER, peaksHandler);
reset();
add(back);
add(front);
}
public function useBitmap(image:Class):void
{
meter = Bitmap(new image).bitmapData.clone();
clone();
}
public function useDraw():void
{
meter.dispose();
meter = null;
drawDisplay();
}
protected function reset():void
{
timer.reset();
levels.length = m_columns;
for (var i:int = 0; i < m_columns; ++i)
levels[i] = 0;
background.alpha = m_backgroundAlpha;
background.rotation = 0;
background.x = 0;
background.y = 0;
foreground.rotation = 0;
foreground.x = 0;
foreground.y = 0;
if (meter)
clone();
else
drawDisplay();
}
public function set x(x:int):void
{
back.x = x;
front.x = x;
}
public function get x():int
{
return front.x;
}
public function set y(y:int):void
{
back.y = y;
front.y = y;
}
public function get y():int
{
return front.y;
}
protected function start():void
{
if (!soundEx)
return;
timer.reset();
timer.start();
}
protected function clone():void
{
sectionWidth = meter.width + m_columnSpacing;
sectionHeight = m_rowSize + m_rowSpacing;
realHeight = meter.height + m_rowSpacing;
var h:int = meter.height, i:int, w:int = m_columns * sectionWidth - m_columnSpacing;
output = new BitmapData(w, h, true, 0);
buffer = output.clone();
output.lock();
destPoint.x = 0;
for (i = 0; i < m_columns; ++i)
{
output.copyPixels(meter, meter.rect, destPoint);
destPoint.x += sectionWidth;
}
m_rows = realHeight / sectionHeight;
if (m_rowSpacing > 0)
{
sourceRect.width = w;
sourceRect.height = m_rowSpacing;
sourceRect.y = h - sectionHeight;
for (i = 0; i < m_rows; ++i)
{
output.fillRect(sourceRect, 0);
sourceRect.y -= sectionHeight;
}
}
output.unlock();
destPoint.x = 0;
sourceRect.width = m_columnSize = meter.width;
input = buffer.clone();
input.threshold(output, output.rect, destPoint, "==", 0xff000000, 0x00ffffff, 0xffffffff, true);
output.fillRect(output.rect, 0);
background.bitmapData = input;
foreground.bitmapData = output;
if (m_direction != UP)
direction = m_direction;
}
public function get width():int
{
return output.width;
}
public function get height():int
{
return output.height;
}
protected function drawDisplay():void
{
sectionWidth = m_columnSize + m_columnSpacing;
sectionHeight = m_rowSize + m_rowSpacing;
realHeight = m_rows * sectionHeight;
var h:int = realHeight - m_rowSpacing, i:int, matrix:Matrix, p:int = m_rowSize, w:int = m_columns * sectionWidth - m_columnSpacing;
if (m_colors.length < 2)
{
canvas.graphics.beginFill(m_colors[0], m_alphas[0]);
}
else
{
matrix = new Matrix();
matrix.createGradientBox(w, h, Math.PI * 0.5, 0, 0);
canvas.graphics.beginGradientFill(GradientType.LINEAR, m_colors, m_alphas, m_ratios, matrix, SpreadMethod.PAD, InterpolationMethod.LINEAR_RGB);
}
canvas.graphics.drawRect(0, 0, w, h);
canvas.graphics.endFill();
for (i = 0; i < m_rows; ++i)
{
canvas.graphics.beginFill(0, 1);
canvas.graphics.drawRect(0, p, w, m_rowSpacing);
canvas.graphics.endFill();
p += sectionHeight;
}
p = m_columnSize;
for (i = 1; i < m_columns; ++i)
{
canvas.graphics.beginFill(0, 1);
canvas.graphics.drawRect(p, 0, m_columnSpacing, h);
canvas.graphics.endFill();
p += sectionWidth;
}
output = new BitmapData(w, h, true, 0);
buffer = output.clone();
output.draw(canvas);
canvas.graphics.clear();
input = buffer.clone();
input.threshold(output, output.rect, destPoint, "==", 0xff000000, 0x00ffffff, 0xffffffff, true);
output.fillRect(output.rect, 0);
background.bitmapData = input;
foreground.bitmapData = output;
sourceRect.width = m_columnSize;
if (m_direction != UP)
direction = m_direction;
}
protected function startHandler(e:Event):void
{
m_soundEx.removeEventListener(SoundEx.SOUND_START, startHandler);
m_soundEx.addEventListener(SoundEx.SOUND_STOP, stopHandler);
m_soundEx.addEventListener(Event.SOUND_COMPLETE, stopHandler);
timer.removeEventListener(TimerEvent.TIMER_COMPLETE, completeHandler);
timer.repeatCount = 0;
start();
}
protected function stopHandler(e:Event):void
{
var i:int, t:Number = 0.0;
for (i = 0; i < m_columns; ++i)
if (levels[i] > t)
t = levels[i];
m_soundEx.removeEventListener(Event.SOUND_COMPLETE, stopHandler);
m_soundEx.removeEventListener(SoundEx.SOUND_STOP, stopHandler);
m_soundEx.addEventListener(SoundEx.SOUND_START, startHandler);
timer.reset();
timer.repeatCount = int(t / m_decay) + 1;
timer.addEventListener(TimerEvent.TIMER_COMPLETE, completeHandler);
timer.start();
}
protected function completeHandler(e:Event):void
{
if (backgroundBeat)
background.alpha = m_backgroundAlpha;
timer.reset();
timer.removeEventListener(TimerEvent.TIMER_COMPLETE, completeHandler);
}
protected function meterHandler(e:TimerEvent):void
{
var h:int, i:int;
spectrum = m_soundEx.getStereoAdd(m_columns);
buffer.fillRect(buffer.rect, 0);
sourceRect.x = 0;
for (i = 0; i < m_columns; ++i)
{
h = int(spectrum[i] * m_rows) * sectionHeight;
sourceRect.height = h;
sourceRect.y = realHeight - h;
buffer.fillRect(sourceRect, 0xff000000);
sourceRect.x += sectionWidth;
}
output.copyPixels(input, input.rect, destPoint, buffer);
if (backgroundBeat)
background.alpha = m_soundEx.peak;
e.updateAfterEvent();
}
protected function decayHandler(e:TimerEvent):void
{
var a:Number, h:int, i:int;
spectrum = m_soundEx.getStereoSampling(m_columns);
output.lock();
sourceRect.x = 0;
buffer.fillRect(buffer.rect, m_decayAlpha);
output.copyPixels(output, output.rect, destPoint, buffer);
buffer.fillRect(buffer.rect, 0);
for (i = 0; i < m_columns; ++i)
{
a = spectrum[i];
if (a > levels[i])
levels[i] = a;
h = int(levels[i] * m_rows) * sectionHeight;
sourceRect.height = h;
sourceRect.y = realHeight - h;
buffer.fillRect(sourceRect, m_peaksAlpha);
sourceRect.x += sectionWidth;
levels[i] -= m_decay;
}
output.copyPixels(input, input.rect, destPoint, buffer, null, true);
output.unlock();
if (backgroundBeat)
background.alpha = m_soundEx.peak;
e.updateAfterEvent();
}
protected function peaksHandler(e:TimerEvent):void
{
var a:Number, h:int, i:int;
spectrum = m_soundEx.getStereoAdd(m_columns);
buffer.fillRect(buffer.rect, 0);
sourceRect.x = 0;
for (i = 0; i < m_columns; ++i)
{
a = spectrum[i];
h = int(a * m_rows) * sectionHeight;
sourceRect.height = h;
sourceRect.y = realHeight - h;
buffer.fillRect(sourceRect, 0xff000000);
if (a > levels[i])
{
levels[i] = a;
}
else
{
h = int(levels[i] * m_rows) * sectionHeight;
sourceRect.y = realHeight - h;
}
sourceRect.height = m_rowSize;
buffer.fillRect(sourceRect, m_peaksAlpha);
sourceRect.x += sectionWidth;
levels[i] -= m_decay;
}
output.copyPixels(input, input.rect, destPoint, buffer);
if (backgroundBeat)
{
background.alpha = m_soundEx.peak;
}
currentPeak = getTimer();
e.updateAfterEvent();
}
override public function update():void
{
// Avoids doing this every single frame, rather only when the meter has peaked
if (currentPeak > lastPeak)
{
lastPeak = currentPeak;
if (foreground)
{
front.pixels = foreground.bitmapData;
front.alpha = foreground.alpha;
}
if (background)
{
back.pixels = background.bitmapData;
back.alpha = background.alpha;
}
}
super.update();
}
public function get soundEx():SoundEx
{
return m_soundEx;
}
public function set soundEx(value:SoundEx):void
{
if (m_soundEx)
m_soundEx.removeEventListener(SoundEx.SOUND_START, startHandler);
m_soundEx = value;
value.addEventListener(SoundEx.SOUND_START, startHandler);
}
public function get direction():String
{
return m_direction;
}
public function set direction(value:String):void
{
if (value == m_direction || !FlxFlectrum[value.toUpperCase()])
return;
switch (value)
{
case UP:
front.angle = 0;
break;
case LEFT:
front.angle = 270;
break;
case DOWN:
front.angle = 180;
break;
case RIGHT:
front.angle = 90;
break;
}
back.angle = front.angle;
m_direction = value;
}
public function get mode():String
{
return m_mode;
}
public function set mode(value:String):void
{
if (value == m_mode || !FlxFlectrum[value.toUpperCase()])
return;
timer.removeEventListener(TimerEvent.TIMER, this[m_mode + "Handler"]);
timer.addEventListener(TimerEvent.TIMER, this[value + "Handler"]);
m_mode = value;
}
public function get delay():int
{
return timer.delay;
}
public function set delay(value:int):void
{
timer.delay = value;
}
public function get columns():int
{
return m_columns;
}
public function set columns(value:int):void
{
if (value == m_columns)
return;
if (value < 2)
value = 2;
else if (value > 256)
value = 256;
m_columns = value;
reset();
}
public function get columnSize():int
{
return m_columnSize;
}
public function set columnSize(value:int):void
{
if (value == m_columnSize)
return;
if (value < 1)
value = 1;
m_columnSize = value;
reset();
}
public function get columnSpacing():int
{
return m_columnSpacing;
}
public function set columnSpacing(value:int):void
{
if (value == m_columnSpacing)
return;
m_columnSpacing = value;
reset();
}
public function get rows():int
{
return m_rows;
}
public function set rows(value:int):void
{
if (value == m_rows)
return;
if (value < 3)
value = 3;
m_rows = value;
reset();
}
public function get rowSize():int
{
return m_rowSize;
}
public function set rowSize(value:int):void
{
if (value == m_rowSize)
return;
if (value < 1)
value = 1;
m_rowSize = value;
reset();
}
public function get rowSpacing():int
{
return m_rowSpacing;
}
public function set rowSpacing(value:int):void
{
if (value == m_rowSpacing)
return;
m_rowSpacing = value;
reset();
}
public function get showBackground():Boolean
{
return m_showBackground;
}
public function set showBackground(value:Boolean):void
{
if (value)
{
back.visible = true;
}
else
{
back.visible = false;
}
if (value == m_showBackground)
return;
m_showBackground = value;
reset();
}
public function get backgroundAlpha():Number
{
return m_backgroundAlpha;
}
public function set backgroundAlpha(value:Number):void
{
background.alpha = m_backgroundAlpha = value;
}
public function get decay():Number
{
return m_decay;
}
public function set decay(value:Number):void
{
if (value < 0)
value = 0;
else if (value > 1)
value = 1;
m_decay = value;
}
public function get decayAlpha():Number
{
return m_decayAlpha / 255;
}
public function set decayAlpha(value:Number):void
{
if (value < 0)
value = 0;
else if (value > 1)
value = 1;
m_decayAlpha = int(value * 255) << 24;
}
public function get peaksAlpha():Number
{
return m_peaksAlpha / 255;
}
public function set peaksAlpha(value:Number):void
{
if (value < 0)
value = 0;
else if (value > 1)
value = 1;
m_peaksAlpha = int(value * 255) << 24;
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxFlod.as
================================================
/**
* FlxFlod
* -- Part of the Flixel Power Tools set
*
* v1.3 Added full FlxFlectrum support
* v1.2 Updated for the Flixel 2.5 Plugin system
*
* @version 1.3 - July 29th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
package org.flixel.plugin.photonstorm
{
import neoart.flectrum.Flectrum;
import neoart.flectrum.SoundEx;
import org.flixel.*;
import neoart.flod.*;
import flash.utils.ByteArray;
import flash.media.SoundTransform;
/**
* FlxFlod adds support for the Flod AS3 Replay library by Christian Corti.
* Flod is an incredibly powerful library allowing you to play tracker music from the Amiga / ST / PC (SoundTracker, ProTracker, etc)
* More information about Flod can be found here: http://www.photonstorm.com/flod
*
* This class works without modifying flixel, however the mute/volume/pause/resume commands won't be hooked into flixel.
* You can either use a patched version of Flixel which is provided in this repository:
* flash-game-dev-tips\Flixel Versions\Flixel v2.43 Patch 1.0
*
* Or you can patch FlxG manually by doing the following:
*
* 1) Add import com.photonstorm.flixel.FlxFlod; at the top of FlxG.as:
* 2) Find the function static public function set mute(Mute:Boolean):void and add this line at the end of it: FlxFlod.mute = Mute;
* 3) Find the function static public function set volume(Volume:Number):void and add this line at the end of it: FlxFlod.volume = Volume;
* 4) Find the function static protected function pauseSounds():void and add this line at the end of it: FlxFlod.pause();
* 5) Find the function static protected function playSounds():void and add this line at the end of it: FlxFlod.resume();
*
* Flixel will now be patched so that any music playing via FlxFlod responds to the global flixel mute, volume and pause controls
*/
public class FlxFlod
{
private static var processor:ModProcessor;
private static var modStream:ByteArray;
private static var soundform:SoundTransform = new SoundTransform();
private static var fadeTimer:FlxDelay;
private static var callbackHooksCreated:Boolean = false;
private static var sound:SoundEx = new SoundEx;
public static var flectrum:FlxFlectrum;
/**
* Starts playback of a tracker module
*
* @param toon The music to play
*
* @return Boolean true if playback started successfully, false if not
*/
public static function playMod(toon:Class):Boolean
{
stopMod();
modStream = new toon() as ByteArray;
processor = new ModProcessor();
if (processor.load(modStream))
{
processor.loopSong = true;
processor.stereo = 0;
processor.play(sound);
if (processor.soundChannel)
{
soundform.volume = FlxG.volume;
processor.soundChannel.soundTransform = soundform;
}
if (callbackHooksCreated == false)
{
FlxG.volumeHandler = updateVolume;
callbackHooksCreated = true;
}
return true;
}
else
{
return false;
}
}
/**
* Creates a Flectrum (VU Meter / Spectrum Analyser)
*
* @param x The x position of the flectrum in game world coordinates
* @param y The y position of the flectrum in game world coordinates
* @param meter A graphic to use for the meter (bar) of the flectrum. Default null uses a solid fill rectangle.
* @param showBackground Display an alpha background behind the meters
* @param backgroundBeat Makes the alpha background pulsate in time to the music
* @param columns The number of columns in the flectrum
* @param columnSize The width of each column in pixels - if you use your own meter graphic this value is ignored
* @param columnSpacing The spacing in pixels between each column (meter) of the flectrum
* @param rows The number of rows in the flectrum
* @param rowSize The height of each row. Overall flectrum height is rowSize + rowSpacing * rows - if you use your own meter graphic this value is ignored
* @param rowSpacing The spacing in pixels between each row of the flectrum - if you use your own meter graphic this value is ignored
*
* @return The FlxFlectrum instance for further modification. Also available via FlxFlod.flectrum
*/
public static function createFlectrum(x:int, y:int, meter:Class = null, showBackground:Boolean = false, backgroundBeat:Boolean = false, columns:int = 15, columnSize:int = 10, columnSpacing:int = 0, rows:int = 32, rowSize:int = 3, rowSpacing:int = 0):FlxFlectrum
{
flectrum = new FlxFlectrum();
flectrum.init(x, y, sound, columns, columnSize, columnSpacing, rows, rowSize, rowSpacing);
if (meter)
{
flectrum.useBitmap(meter);
}
flectrum.showBackground = showBackground;
flectrum.backgroundBeat = backgroundBeat;
return flectrum;
}
/**
* Pauses playback of this module, if started
*/
public static function pause():void
{
if (processor)
{
processor.pause();
}
}
/**
* Resumes playback of this module if paused
*/
public static function resume():void
{
if (processor)
{
processor.resume();
}
}
/**
* Stops playback of this module, if started
*/
public static function stopMod():void
{
if (processor)
{
processor.stop();
}
}
/**
* Toggles playback mute
*/
public static function set mute(Mute:Boolean):void
{
if (processor)
{
if (Mute)
{
if (processor.soundChannel)
{
soundform.volume = 0;
processor.soundChannel.soundTransform = soundform;
}
}
else
{
if (processor.soundChannel)
{
soundform.volume = FlxG.volume;
processor.soundChannel.soundTransform = soundform;
}
}
}
}
/**
* Called by FlxG when the volume is adjusted in-game
*
* @param Volume
*/
public static function updateVolume(Volume:Number):void
{
volume = Volume;
}
/**
* Sets the playback volume directly (usually controlled by FlxG.volume)
*/
public static function set volume(Volume:Number):void
{
if (processor)
{
if (processor.soundChannel)
{
soundform.volume = Volume;
processor.soundChannel.soundTransform = soundform;
}
}
}
/**
* Is a tune already playing?
*/
public static function get isPlaying():Boolean
{
if (processor)
{
return processor.isPlaying;
}
else
{
return false;
}
}
/**
* Is a tune paused?
*/
public static function get isPaused():Boolean
{
if (processor)
{
return processor.isPaused;
}
else
{
return false;
}
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxGradient.as
================================================
/**
* FlxGradient
* -- Part of the Flixel Power Tools set
*
* v1.6 Fixed bug where gradients with chunk sizes > 1 would ignore alpha values
* v1.5 Alpha values used in the gradient map
* v1.4 Updated for the Flixel 2.5 Plugin system
*
* @version 1.6 - May 9th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
* @see Requires FlxMath
*/
package org.flixel.plugin.photonstorm
{
import flash.geom.Point;
import flash.geom.Rectangle;
import org.flixel.*;
import flash.display.Bitmap;
import flash.geom.Matrix;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.GradientType;
import flash.display.SpreadMethod;
import flash.display.InterpolationMethod;
/**
* Adds a set of color gradient creation / rendering functions
*/
public class FlxGradient
{
public function FlxGradient()
{
}
public static function createGradientMatrix(width:int, height:int, colors:Array, chunkSize:int = 1, rotation:int = 90):Object
{
var gradientMatrix:Matrix = new Matrix();
// Rotation (in radians) that the gradient is rotated
var rot:Number = FlxMath.asRadians(rotation);
// Last 2 values = horizontal and vertical shift (in pixels)
if (chunkSize == 1)
{
gradientMatrix.createGradientBox(width, height, rot, 0, 0);
}
else
{
gradientMatrix.createGradientBox(width, height / chunkSize, rot, 0, 0);
}
// Create the alpha and ratio arrays
var alpha:Array = new Array();
for (var ai:int = 0; ai < colors.length; ai++)
{
alpha.push(FlxColor.getAlphaFloat(colors[ai]));
}
var ratio:Array = new Array();
if (colors.length == 2)
{
ratio[0] = 0;
ratio[1] = 255;
}
else
{
// Spread value
var spread:int = 255 / (colors.length - 1);
ratio.push(0);
for (var ri:int = 1; ri < colors.length - 1; ri++)
{
ratio.push(ri * spread);
}
ratio.push(255);
}
return { matrix: gradientMatrix, alpha: alpha, ratio: ratio };
}
public static function createGradientArray(width:int, height:int, colors:Array, chunkSize:int = 1, rotation:int = 90, interpolate:Boolean = true):Array
{
var data:BitmapData = createGradientBitmapData(width, height, colors, chunkSize, rotation, interpolate);
var result:Array = new Array();
for (var y:int = 0; y < data.height; y++)
{
result.push(data.getPixel32(0, y));
}
return result;
}
/**
* Creates an FlxSprite of the given width/height with a colour gradient flowing through it.
*
* @param width The width of the FlxSprite (and therefore gradient)
* @param height The height of the FlxSprite (and therefore gradient)
* @param colors An array of colour values for the gradient to cycle through
* @param chunkSize If you want a more old-skool looking chunky gradient, increase this value!
* @param rotation Angle of the gradient in degrees. 90 = top to bottom, 180 = left to right. Any angle is valid
* @param interpolate Interpolate the colours? True uses RGB interpolation, false uses linear RGB
*
* @return An FlxSprite containing your gradient (if valid parameters given!)
*/
public static function createGradientFlxSprite(width:int, height:int, colors:Array, chunkSize:int = 1, rotation:int = 90, interpolate:Boolean = true):FlxSprite
{
var data:BitmapData = createGradientBitmapData(width, height, colors, chunkSize, rotation, interpolate);
var dest:FlxSprite = new FlxSprite().makeGraphic(width, height);
dest.pixels = data;
return dest;
}
public static function createGradientBitmapData(width:int, height:int, colors:Array, chunkSize:int = 1, rotation:int = 90, interpolate:Boolean = true):BitmapData
{
// Sanity checks
if (width < 1)
{
width = 1;
}
if (height < 1)
{
height = 1;
}
var gradient:Object = createGradientMatrix(width, height, colors, chunkSize, rotation);
var s:Shape = new Shape();
if (interpolate)
{
s.graphics.beginGradientFill(GradientType.LINEAR, colors, gradient.alpha, gradient.ratio, gradient.matrix, SpreadMethod.PAD, InterpolationMethod.RGB, 0);
}
else
{
s.graphics.beginGradientFill(GradientType.LINEAR, colors, gradient.alpha, gradient.ratio, gradient.matrix, SpreadMethod.PAD, InterpolationMethod.LINEAR_RGB, 0);
}
if (chunkSize == 1)
{
s.graphics.drawRect(0, 0, width, height);
}
else
{
s.graphics.drawRect(0, 0, width, height / chunkSize);
}
var data:BitmapData = new BitmapData(width, height, true, 0x0);
if (chunkSize == 1)
{
data.draw(s);
}
else
{
var tempBitmap:Bitmap = new Bitmap(new BitmapData(width, height / chunkSize, true, 0x0));
tempBitmap.bitmapData.draw(s);
tempBitmap.scaleY = chunkSize;
var sM:Matrix = new Matrix();
sM.scale(tempBitmap.scaleX, tempBitmap.scaleY);
data.draw(tempBitmap, sM);
}
return data;
}
/**
* Creates a new gradient and overlays that on-top of the given FlxSprite at the destX/destY coordinates (default 0,0)
* Use low alpha values in the colours to have the gradient overlay and not destroy the image below
*
* @param dest The FlxSprite to overlay the gradient onto
* @param width The width of the FlxSprite (and therefore gradient)
* @param height The height of the FlxSprite (and therefore gradient)
* @param colors An array of colour values for the gradient to cycle through
* @param destX The X offset the gradient is drawn at (default 0)
* @param destY The Y offset the gradient is drawn at (default 0)
* @param chunkSize If you want a more old-skool looking chunky gradient, increase this value!
* @param rotation Angle of the gradient in degrees. 90 = top to bottom, 180 = left to right. Any angle is valid
* @param interpolate Interpolate the colours? True uses RGB interpolation, false uses linear RGB
* @return The composited FlxSprite (for chaining, if you need)
*/
public static function overlayGradientOnFlxSprite(dest:FlxSprite, width:int, height:int, colors:Array, destX:int = 0, destY:int = 0, chunkSize:int = 1, rotation:int = 90, interpolate:Boolean = true):FlxSprite
{
if (width > dest.width)
{
width = dest.width;
}
if (height > dest.height)
{
height = dest.height;
}
var source:FlxSprite = createGradientFlxSprite(width, height, colors, chunkSize, rotation, interpolate);
dest.stamp(source, destX, destY);
return dest;
}
/**
* Creates a new gradient and overlays that on-top of the given BitmapData at the destX/destY coordinates (default 0,0)
* Use low alpha values in the colours to have the gradient overlay and not destroy the image below
*
* @param dest The BitmapData to overlay the gradient onto
* @param width The width of the FlxSprite (and therefore gradient)
* @param height The height of the FlxSprite (and therefore gradient)
* @param colors An array of colour values for the gradient to cycle through
* @param destX The X offset the gradient is drawn at (default 0)
* @param destY The Y offset the gradient is drawn at (default 0)
* @param chunkSize If you want a more old-skool looking chunky gradient, increase this value!
* @param rotation Angle of the gradient in degrees. 90 = top to bottom, 180 = left to right. Any angle is valid
* @param interpolate Interpolate the colours? True uses RGB interpolation, false uses linear RGB
* @return The composited BitmapData
*/
public static function overlayGradientOnBitmapData(dest:BitmapData, width:int, height:int, colors:Array, destX:int = 0, destY:int = 0, chunkSize:int = 1, rotation:int = 90, interpolate:Boolean = true):BitmapData
{
if (width > dest.width)
{
width = dest.width;
}
if (height > dest.height)
{
height = dest.height;
}
var source:BitmapData = createGradientBitmapData(width, height, colors, chunkSize, rotation, interpolate);
dest.copyPixels(source, new Rectangle(0, 0, source.width, source.height), new Point(destX, destY), null, null, true);
return dest;
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxGridOverlay.as
================================================
/**
* FlxGridOverlay
* -- Part of the Flixel Power Tools set
*
* v1.1 Updated for the Flixel 2.5 Plugin system
*
* @version 1.1 - April 23rd 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
package org.flixel.plugin.photonstorm
{
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import org.flixel.*;
public class FlxGridOverlay
{
public function FlxGridOverlay()
{
}
/**
* Creates an FlxSprite of the given width and height filled with a checkerboard pattern.
* Each grid cell is the specified width and height, and alternates between two colors.
* If alternate is true each row of the pattern will be offset, for a proper checkerboard style. If false each row will be the same colour, creating a striped-pattern effect.
* So to create an 8x8 grid you'd call create(8,8)
*
* @param cellWidth The grid cell width
* @param cellHeight The grid cell height
* @param width The width of the FlxSprite. If -1 it will be the size of the game (FlxG.width)
* @param height The height of the FlxSprite. If -1 it will be the size of the game (FlxG.height)
* @param addLegend TODO
* @param alternate Should the pattern alternate on each new row? Default true = checkerboard effect. False = vertical stripes
* @param color1 The first fill colour in 0xAARRGGBB format
* @param color2 The second fill colour in 0xAARRGGBB format
*
* @return FlxSprite of given width/height
*/
public static function create(cellWidth:int, cellHeight:int, width:int = -1, height:int = -1, addLegend:Boolean = false, alternate:Boolean = true, color1:uint = 0xffe7e6e6, color2:uint = 0xffd9d5d5):FlxSprite
{
if (width == -1)
{
width = FlxG.width;
}
if (height == -1)
{
height = FlxG.height;
}
if (width < cellWidth || height < cellHeight)
{
return null;
}
var grid:BitmapData = createGrid(cellWidth, cellHeight, width, height, alternate, color1, color2);
var output:FlxSprite = new FlxSprite().makeGraphic(width, height);
output.pixels = grid;
output.dirty = true;
return output;
}
/**
* Creates a checkerboard pattern of the given width/height and overlays it onto the given FlxSprite.
* Each grid cell is the specified width and height, and alternates between two colors.
* If alternate is true each row of the pattern will be offset, for a proper checkerboard style. If false each row will be the same colour, creating a striped-pattern effect.
* So to create an 8x8 grid you'd call create(8,8,
*
* @param source The FlxSprite you wish to draw the grid on-top of. This updates its pixels value, not just the current frame (don't use animated sprites!)
* @param cellWidth The grid cell width
* @param cellHeight The grid cell height
* @param width The width of the FlxSprite. If -1 it will be the size of the game (FlxG.width)
* @param height The height of the FlxSprite. If -1 it will be the size of the game (FlxG.height)
* @param addLegend TODO
* @param alternate Should the pattern alternate on each new row? Default true = checkerboard effect. False = vertical stripes
* @param color1 The first fill colour in 0xAARRGGBB format
* @param color2 The second fill colour in 0xAARRGGBB format
*
* @return The modified source FlxSprite
*/
public static function overlay(source:FlxSprite, cellWidth:int, cellHeight:int, width:int = -1, height:int = -1, addLegend:Boolean = false, alternate:Boolean = true, color1:uint = 0x88e7e6e6, color2:uint = 0x88d9d5d5):FlxSprite
{
if (width == -1)
{
width = FlxG.width;
}
if (height == -1)
{
height = FlxG.height;
}
if (width < cellWidth || height < cellHeight)
{
return null;
}
var grid:BitmapData = createGrid(cellWidth, cellHeight, width, height, alternate, color1, color2);
var pixels:BitmapData = source.pixels;
pixels.copyPixels(grid, new Rectangle(0, 0, width, height), new Point(0, 0), null, null, true);
source.pixels = pixels;
return source;
}
public static function addLegend(source:FlxSprite, cellWidth:int, cellHeight:int, xAxis:Boolean = true, yAxis:Boolean = true):FlxSprite
{
if (cellWidth > source.width)
{
throw Error("cellWidth larger than FlxSprites width");
return source;
}
if (cellHeight > source.height)
{
throw Error("cellHeight larger than FlxSprites height");
return source;
}
if (source.width < cellWidth || source.height < cellHeight)
{
throw Error("source FlxSprite width or height smaller than requested cell width or height");
return source;
}
// Valid cell width/height and source to work on
return source;
}
public static function createGrid(cellWidth:int, cellHeight:int, width:int, height:int, alternate:Boolean, color1:uint, color2:uint):BitmapData
{
// How many cells can we fit into the width/height? (round it UP if not even, then trim back)
var rowColor:uint = color1;
var lastColor:uint = color1;
var grid:BitmapData = new BitmapData(width, height, true);
// If there aren't an even number of cells in a row then we need to swap the lastColor value
for (var y:int = 0; y <= height; y += cellHeight)
{
if (y > 0 && lastColor == rowColor && alternate)
{
(lastColor == color1) ? lastColor = color2 : lastColor = color1;
}
else if (y > 0 && lastColor != rowColor && alternate == false)
{
(lastColor == color2) ? lastColor = color1 : lastColor = color2;
}
for (var x:int = 0; x <= width; x += cellWidth)
{
if (x == 0)
{
rowColor = lastColor;
}
grid.fillRect(new Rectangle(x, y, cellWidth, cellHeight), lastColor);
if (lastColor == color1)
{
lastColor = color2;
}
else
{
lastColor = color1;
}
}
}
return grid;
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxLinkedGroup.as
================================================
package org.flixel.plugin.photonstorm
{
import org.flixel.FlxGroup;
import org.flixel.FlxSprite;
import flash.utils.getTimer;
public class FlxLinkedGroup extends FlxGroup
{
//private var queue
public function FlxLinkedGroup(MaxSize:uint = 0)
{
super(MaxSize);
}
public function addX(newX:int):void
{
for each (var s:FlxSprite in members)
{
s.x += newX;
}
}
public function angle(newX:int):void
{
for each (var s:FlxSprite in members)
{
s.angle += newX;
}
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxMath.as
================================================
/**
* FlxMath
* -- Part of the Flixel Power Tools set
*
* v1.7 Added mouseInFlxRect
* v1.6 Added wrapAngle, angleLimit and more documentation
* v1.5 Added pointInCoordinates, pointInFlxRect and pointInRectangle
* v1.4 Updated for the Flixel 2.5 Plugin system
*
* @version 1.7 - June 28th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
package org.flixel.plugin.photonstorm
{
import flash.geom.Rectangle;
import org.flixel.*;
/**
* Adds a set of fast Math functions and extends a few commonly used ones
*/
public class FlxMath
{
public static var getrandmax:int = int.MAX_VALUE;
private static var mr:uint = 0;
private static var cosTable:Array = new Array;
private static var sinTable:Array = new Array;
private static var coefficient1:Number = Math.PI / 4;
private static const RADTODEG:Number = 180 / Math.PI;
private static const DEGTORAD:Number = Math.PI / 180;
public function FlxMath()
{
}
/**
* Returns true if the given x/y coordinate is within the given rectangular block
*
* @param pointX The X value to test
* @param pointY The Y value to test
* @param rectX The X value of the region to test within
* @param rectY The Y value of the region to test within
* @param rectWidth The width of the region to test within
* @param rectHeight The height of the region to test within
*
* @return true if pointX/pointY is within the region, otherwise false
*/
public static function pointInCoordinates(pointX:int, pointY:int, rectX:int, rectY:int, rectWidth:int, rectHeight:int):Boolean
{
if (pointX >= rectX && pointX <= (rectX + rectWidth))
{
if (pointY >= rectY && pointY <= (rectY + rectHeight))
{
return true;
}
}
return false;
}
/**
* Returns true if the given x/y coordinate is within the given rectangular block
*
* @param pointX The X value to test
* @param pointY The Y value to test
* @param rect The FlxRect to test within
* @return true if pointX/pointY is within the FlxRect, otherwise false
*/
public static function pointInFlxRect(pointX:int, pointY:int, rect:FlxRect):Boolean
{
if (pointX >= rect.x && pointX <= rect.right && pointY >= rect.y && pointY <= rect.bottom)
{
return true;
}
return false;
}
/**
* Returns true if the mouse world x/y coordinate are within the given rectangular block
*
* @param useWorldCoords If true the world x/y coordinates of the mouse will be used, otherwise screen x/y
* @param rect The FlxRect to test within. If this is null for any reason this function always returns true.
*
* @return true if mouse is within the FlxRect, otherwise false
*/
public static function mouseInFlxRect(useWorldCoords:Boolean, rect:FlxRect):Boolean
{
if (rect == null)
{
return true;
}
if (useWorldCoords)
{
return pointInFlxRect(FlxG.mouse.x, FlxG.mouse.y, rect);
}
else
{
return pointInFlxRect(FlxG.mouse.screenX, FlxG.mouse.screenY, rect);
}
}
/**
* Returns true if the given x/y coordinate is within the Rectangle
*
* @param pointX The X value to test
* @param pointY The Y value to test
* @param rect The Rectangle to test within
* @return true if pointX/pointY is within the Rectangle, otherwise false
*/
public static function pointInRectangle(pointX:int, pointY:int, rect:Rectangle):Boolean
{
if (pointX >= rect.x && pointX <= rect.right && pointY >= rect.y && pointY <= rect.bottom)
{
return true;
}
return false;
}
/**
* A faster (but much less accurate) version of Math.atan2(). For close range / loose comparisons this works very well,
* but avoid for long-distance or high accuracy simulations.
* Based on: http://blog.gamingyourway.com/PermaLink,guid,78341247-3344-4a7a-acb2-c742742edbb1.aspx
*
* Computes and returns the angle of the point y/x in radians, when measured counterclockwise from a circle's x axis
* (where 0,0 represents the center of the circle). The return value is between positive pi and negative pi.
* Note that the first parameter to atan2 is always the y coordinate.
*
* @param y The y coordinate of the point
* @param x The x coordinate of the point
* @return The angle of the point x/y in radians
*/
public static function atan2(y:Number, x:Number):Number
{
var absY:Number = y;
var coefficient2:Number = 3 * coefficient1;
var r:Number;
var angle:Number;
if (absY < 0)
{
absY = -absY;
}
if (x >= 0)
{
r = (x - absY) / (x + absY);
angle = coefficient1 - coefficient1 * r;
}
else
{
r = (x + absY) / (absY - x);
angle = coefficient2 - coefficient1 * r;
}
return y < 0 ? -angle : angle;
}
/**
* Generate a sine and cosine table simultaneously and extremely quickly. Based on research by Franky of scene.at
*
* The parameters allow you to specify the length, amplitude and frequency of the wave. Once you have called this function
* you should get the results via getSinTable() and getCosTable(). This generator is fast enough to be used in real-time.
*
* @param length The length of the wave
* @param sinAmplitude The amplitude to apply to the sine table (default 1.0) if you need values between say -+ 125 then give 125 as the value
* @param cosAmplitude The amplitude to apply to the cosine table (default 1.0) if you need values between say -+ 125 then give 125 as the value
* @param frequency The frequency of the sine and cosine table data
* @return Returns the sine table
* @see getSinTable
* @see getCosTable
*/
public static function sinCosGenerator(length:uint, sinAmplitude:Number = 1.0, cosAmplitude:Number = 1.0, frequency:Number = 1.0):Array
{
var sin:Number = sinAmplitude;
var cos:Number = cosAmplitude;
var frq:Number = frequency * Math.PI / length;
cosTable = new Array();
sinTable = new Array();
for (var c:uint = 0; c < length; c++)
{
cos -= sin * frq;
sin += cos * frq;
cosTable[c] = cos;
sinTable[c] = sin;
}
return sinTable;
}
/**
* Returns the sine table generated by sinCosGenerator(), or an empty array object if not yet populated
* @return Array of sine wave data
* @see sinCosGenerator
*/
public static function getSinTable():Array
{
return sinTable;
}
/**
* Returns the cosine table generated by sinCosGenerator(), or an empty array object if not yet populated
* @return Array of cosine wave data
* @see sinCosGenerator
*/
public static function getCosTable():Array
{
return cosTable;
}
/**
* A faster version of Math.sqrt
*
* Computes and returns the square root of the specified number.
*
* @link http://osflash.org/as3_speed_optimizations#as3_speed_tests
* @param val A number greater than or equal to 0
* @return If the parameter val is greater than or equal to zero, a number; otherwise NaN (not a number).
*/
public static function sqrt(val:Number):Number
{
if (isNaN(val))
{
return NaN;
}
var thresh:Number = 0.002;
var b:Number = val * 0.25;
var a:Number;
var c:Number;
if (val == 0)
{
return 0;
}
do {
c = val / b;
b = (b + c) * 0.5;
a = b - c;
if (a < 0) a = -a;
}
while (a > thresh);
return b;
}
/**
* Generates a small random number between 0 and 65535 very quickly
*
* Generates a small random number between 0 and 65535 using an extremely fast cyclical generator,
* with an even spread of numbers. After the 65536th call to this function the value resets.
*
* @return A pseudo random value between 0 and 65536 inclusive.
*/
public static function miniRand():int
{
var result:uint = mr;
result++;
result *= 75;
result %= 65537;
result--;
mr++;
if (mr == 65536)
{
mr = 0;
}
return result;
}
/**
* Generate a random integer
*
* If called without the optional min, max arguments rand() returns a peudo-random integer between 0 and getrandmax().
* If you want a random number between 5 and 15, for example, (inclusive) use rand(5, 15)
* Parameter order is insignificant, the return will always be between the lowest and highest value.
*
* @param min The lowest value to return (default: 0)
* @param max The highest value to return (default: getrandmax)
* @param excludes An Array of integers that will NOT be returned (default: null)
* @return A pseudo-random value between min (or 0) and max (or getrandmax, inclusive)
*/
public static function rand(min:Number = NaN, max:Number = NaN, excludes:Array = null):int
{
if (isNaN(min))
{
min = 0;
}
if (isNaN(max))
{
max = getrandmax;
}
if (min == max)
{
return min;
}
if (excludes != null)
{
// Sort the exclusion array
excludes.sort(Array.NUMERIC);
var result:int;
do {
if (min < max)
{
result = min + Math.floor(Math.random() * (max + 1 - min)); // Math.floor and adding 1 makes this inclusive
}
else
{
result = max + Math.floor(Math.random() * (min + 1 - max));
}
}
while (excludes.indexOf(result) >= 0);
return result;
}
else
{
// Reverse check
if (min < max)
{
return min + Math.floor(Math.random() * (max + 1 - min));
}
else
{
return max + Math.floor(Math.random() * (min + 1 - max));
}
}
}
/**
* Generate a random float (number)
*
* If called without the optional min, max arguments rand() returns a peudo-random float between 0 and getrandmax().
* If you want a random number between 5 and 15, for example, (inclusive) use rand(5, 15)
* Parameter order is insignificant, the return will always be between the lowest and highest value.
*
* @param min The lowest value to return (default: 0)
* @param max The highest value to return (default: getrandmax)
* @return A pseudo random value between min (or 0) and max (or getrandmax, inclusive)
*/
public static function randFloat(min:Number = NaN, max:Number = NaN):Number
{
if (isNaN(min))
{
min = 0;
}
if (isNaN(max))
{
max = getrandmax;
}
if (min == max)
{
return min;
}
else if (min < max)
{
return min + (Math.random() * (max - min + 1));
}
else
{
return max + (Math.random() * (min - max + 1));
}
}
/**
* Generate a random boolean result based on the chance value
*
* Returns true or false based on the chance value (default 50%). For example if you wanted a player to have a 30% chance
* of getting a bonus, call chanceRoll(30) - true means the chance passed, false means it failed.
*
* @param chance The chance of receiving the value. Should be given as a uint between 0 and 100 (effectively 0% to 100%)
* @return true if the roll passed, or false
*/
public static function chanceRoll(chance:uint = 50):Boolean
{
if (chance <= 0)
{
return false;
}
else if (chance >= 100)
{
return true;
}
else
{
if (Math.random() * 100 >= chance)
{
return false;
}
else
{
return true;
}
}
}
/**
* Adds the given amount to the value, but never lets the value go over the specified maximum
*
* @param value The value to add the amount to
* @param amount The amount to add to the value
* @param max The maximum the value is allowed to be
* @return The new value
*/
public static function maxAdd(value:int, amount:int, max:int):int
{
value += amount;
if (value > max)
{
value = max;
}
return value;
}
/**
* Adds value to amount and ensures that the result always stays between 0 and max, by wrapping the value around.
* Values must be positive integers, and are passed through Math.abs
*
* @param value The value to add the amount to
* @param amount The amount to add to the value
* @param max The maximum the value is allowed to be
* @return The wrapped value
*/
public static function wrapValue(value:int, amount:int, max:int):int
{
var diff:int;
value = Math.abs(value);
amount = Math.abs(amount);
max = Math.abs(max);
diff = (value + amount) % max;
return diff;
}
/**
* Finds the length of the given vector
*
* @param dx
* @param dy
*
* @return
*/
public static function vectorLength(dx:Number, dy:Number):Number
{
return Math.sqrt(dx * dx + dy * dy);
}
/**
* Finds the dot product value of two vectors
*
* @param ax Vector X
* @param ay Vector Y
* @param bx Vector X
* @param by Vector Y
*
* @return Dot product
*/
public static function dotProduct(ax:Number, ay:Number, bx:Number, by:Number):Number
{
return ax * bx + ay * by;
}
/**
* Randomly returns either a 1 or -1
*
* @return 1 or -1
*/
public static function randomSign():Number
{
return (Math.random() > 0.5) ? 1 : -1;
}
/**
* Returns true if the number given is odd.
*
* @param n The number to check
*
* @return True if the given number is odd. False if the given number is even.
*/
public static function isOdd(n:Number):Boolean
{
if (n & 1)
{
return true;
}
else
{
return false;
}
}
/**
* Returns true if the number given is even.
*
* @param n The number to check
*
* @return True if the given number is even. False if the given number is odd.
*/
public static function isEven(n:Number):Boolean
{
if (n & 1)
{
return false;
}
else
{
return true;
}
}
/**
* Keeps an angle value between -180 and +180
* Should be called whenever the angle is updated on the FlxSprite to stop it from going insane.
*
* @param angle The angle value to check
*
* @return The new angle value, returns the same as the input angle if it was within bounds
*/
public static function wrapAngle(angle:Number):int
{
var result:int = int(angle);
if (angle > 180)
{
result = -180;
}
else if (angle < -180)
{
result = 180;
}
return result;
}
/**
* Keeps an angle value between the given min and max values
*
* @param angle The angle value to check. Must be between -180 and +180
* @param min The minimum angle that is allowed (must be -180 or greater)
* @param max The maximum angle that is allowed (must be 180 or less)
*
* @return The new angle value, returns the same as the input angle if it was within bounds
*/
public static function angleLimit(angle:int, min:int, max:int):int
{
var result:int = angle;
if (angle > max)
{
result = max;
}
else if (angle < min)
{
result = min;
}
return result;
}
/**
* Converts a Radian value into a Degree
*
* Converts the radians value into degrees and returns
*
* @param radians The value in radians
* @return Number Degrees
*/
public static function asDegrees(radians:Number):Number
{
return radians * RADTODEG;
}
/**
* Converts a Degrees value into a Radian
*
* Converts the degrees value into radians and returns
*
* @param degrees The value in degrees
* @return Number Radians
*/
public static function asRadians(degrees:Number):Number
{
return degrees * DEGTORAD;
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxMouseControl.as
================================================
/**
* FlxMouseControl
* -- Part of the Flixel Power Tools set
*
* v1.2 Added Mouse Zone, Mouse Speed and refactored addToStack process
* v1.1 Moved to a native plugin
* v1.0 First release
*
* @version 1.2 - July 28th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
package org.flixel.plugin.photonstorm
{
import org.flixel.*;
public class FlxMouseControl extends FlxBasic
{
/**
* Use with sort() to sort in ascending order.
*/
public static const ASCENDING:int = -1;
/**
* Use with sort() to sort in descending order.
*/
public static const DESCENDING:int = 1;
/**
* The value that the FlxExtendedSprites are sorted by before deciding which is "on-top" for click select
*/
public static var sortIndex:String = "y";
/**
* The sorting order. If the sortIndex is "y" and the order is ASCENDING then a sprite with a Y value of 200 would be "on-top" of one with a Y value of 100.
*/
public static var sortOrder:int = ASCENDING;
/**
* Is the mouse currently dragging a sprite? If you have just clicked but NOT yet moved the mouse then this might return false.
*/
public static var isDragging:Boolean = false;
/**
* The FlxExtendedSprite that is currently being dragged, if any.
*/
public static var dragTarget:FlxExtendedSprite;
/**
* The FlxExtendedSprite that currently has the mouse button pressed on it
*/
public static var clickTarget:FlxExtendedSprite;
private static var clickStack:Array = new Array;
private static var clickCoords:FlxPoint;
private static var hasClickTarget:Boolean = false;
private static var oldX:int;
private static var oldY:int;
/**
* The speed the mouse is moving on the X axis in pixels per frame
*/
public static var speedX:int;
/**
* The speed the mouse is moving on the Y axis in pixels per frame
*/
public static var speedY:int;
/**
* The mouse can be set to only be active within a specific FlxRect region of the game world.
* If outside this FlxRect no clicks, drags or throws will be processed.
* If the mouse leaves this region while still dragging then the sprite is automatically dropped and its release handler is called.
* Set the FlxRect to null to disable the zone.
*/
public static var mouseZone:FlxRect;
/**
* Instead of using a mouseZone (which is calculated in world coordinates) you can limit the mouse to the FlxG.camera.deadzone area instead.
* If set to true the mouse will use the camera deadzone. If false (or the deadzone is null) no check will take place.
* Note that this takes priority over the mouseZone above. If the mouseZone and deadzone are set, the deadzone is used.
*/
public static var linkToDeadZone:Boolean = false;
public function FlxMouseControl()
{
}
/**
* Adds the given FlxExtendedSprite to the stack of potential sprites that were clicked, the stack is then sorted and the final sprite is selected from that
*
* @param item The FlxExtendedSprite that was clicked by the mouse
*/
public static function addToStack(item:FlxExtendedSprite):void
{
if (mouseZone is FlxRect)
{
if (FlxMath.pointInFlxRect(FlxG.mouse.x, FlxG.mouse.y, mouseZone) == true)
{
clickStack.push(item);
}
}
else
{
clickStack.push(item);
}
}
/**
* Main Update Loop - checks mouse status and updates FlxExtendedSprites accordingly
*/
override public function update():void
{
// Update mouse speed
speedX = FlxG.mouse.screenX - oldX;
speedY = FlxG.mouse.screenY - oldY;
oldX = FlxG.mouse.screenX;
oldY = FlxG.mouse.screenY;
// Is the mouse currently pressed down on a target?
if (hasClickTarget)
{
if (FlxG.mouse.pressed())
{
// Has the mouse moved? If so then we're candidate for a drag
if (isDragging == false && clickTarget.draggable && (clickCoords.x != FlxG.mouse.x || clickCoords.y != FlxG.mouse.y))
{
// Drag on
isDragging = true;
dragTarget = clickTarget;
dragTarget.startDrag();
}
}
else
{
releaseMouse();
}
if (linkToDeadZone)
{
if (FlxMath.mouseInFlxRect(false, FlxG.camera.deadzone) == false)
{
releaseMouse();
}
}
else if (FlxMath.mouseInFlxRect(true, mouseZone) == false)
{
// Is a mouse zone enabled? In which case check if we're still in it
releaseMouse();
}
}
else
{
// No target, but is the mouse down?
if (FlxG.mouse.justPressed())
{
clickStack.length = 0;
}
// If you are wondering how the brand new array can have anything in it by now, it's because FlxExtendedSprite
// adds itself to the clickStack
if (FlxG.mouse.pressed() && clickStack.length > 0)
{
assignClickedSprite();
}
}
}
/**
* Internal function used to release the click / drag targets and reset the mouse state
*/
private function releaseMouse():void
{
// Mouse is no longer down, so tell the click target it's free - this will also stop dragging if happening
clickTarget.mouseReleasedHandler();
hasClickTarget = false;
clickTarget = null;
isDragging = false;
dragTarget = null;
}
/**
* Once the clickStack is created this sorts it and then picks the sprite with the highest priority (based on sortIndex and sortOrder)
*/
private function assignClickedSprite():void
{
// If there is more than one potential target then sort them
if (clickStack.length > 1)
{
clickStack.sort(sortHandler);
}
clickTarget = clickStack.pop();
clickCoords = clickTarget.point;
hasClickTarget = true;
clickTarget.mousePressedHandler();
clickStack.length = 0;
}
/**
* Helper function for the sort process.
*
* @param item1 The first object being sorted.
* @param item2 The second object being sorted.
*
* @return An integer value: -1 (item1 before item2), 0 (same), or 1 (item1 after item2)
*/
private function sortHandler(item1:FlxExtendedSprite, item2:FlxExtendedSprite):int
{
if (item1[sortIndex] < item2[sortIndex])
{
return sortOrder;
}
else if (item1[sortIndex] > item2[sortIndex])
{
return -sortOrder;
}
return 0;
}
/**
* Removes all references to any click / drag targets and resets this class
*/
public static function clear():void
{
hasClickTarget = false;
if (clickTarget)
{
clickTarget.mouseReleasedHandler();
}
clickTarget = null;
isDragging = false;
if (dragTarget)
{
dragTarget.stopDrag();
}
speedX = 0;
speedY = 0;
dragTarget = null;
mouseZone = null;
linkToDeadZone = false;
}
/**
* Runs when this plugin is destroyed
*/
override public function destroy():void
{
clear();
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxPowerTools.as
================================================
/**
* Flixel Power Tools
*
* Version information and constants the other classes in this package can reference
*
* @version 1.9
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
package org.flixel.plugin.photonstorm
{
public class FlxPowerTools
{
public static const LIBRARY_NAME:String = "flixel power tools";
public static const LIBRARY_MAJOR_VERSION:int = 1;
public static const LIBRARY_MINOR_VERSION:int = 9;
public function FlxPowerTools()
{
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxScreenGrab.as
================================================
/**
* FlxScreenGrab
* -- Part of the Flixel Power Tools set
*
* v1.0 Updated for the Flixel 2.5 Plugin system
*
* @version 1.0 - April 28th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
package org.flixel.plugin.photonstorm
{
import org.flixel.*;
import flash.geom.Rectangle;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.net.FileReference;
import flash.utils.ByteArray;
import flash.utils.getTimer;
/**
* Captures a screen grab of the game and stores it locally, optionally saving as a PNG.
*/
public class FlxScreenGrab extends FlxBasic
{
public static var screenshot:Bitmap;
private static var hotkey:String = "";
private static var autoSave:Boolean = false;
private static var autoHideMouse:Boolean = false;
private static var region:Rectangle;
public function FlxScreenGrab()
{
}
/**
* Defines the region of the screen that should be captured. If you need it to be a fixed location then use this.
* If you want to grab the whole SWF size, you don't need to set this as that is the default.
* Remember that if your game is running in a zoom mode > 1 you need to account for this here.
*
* @param x The x coordinate (in Flash display space, not Flixel game world)
* @param y The y coordinate (in Flash display space, not Flixel game world)
* @param width The width of the grab region
* @param height The height of the grab region
*/
public static function defineCaptureRegion(x:int, y:int, width:int, height:int):void
{
region = new Rectangle(x, y, width, height);
}
/**
* Clears a previously defined capture region
*/
public static function clearCaptureRegion():void
{
region = null;
}
/**
* Specify which key will capture a screen shot. Use the String value of the key in the same way FlxG.keys does (so "F1" for example)
* Optionally save the image to a file immediately. This uses the file systems "Save as" dialog window and pauses your game during the process.
*
* @param key String The key you press to capture the screen (i.e. "F1", "SPACE", etc - see system.input.Keyboard.as source for reference)
* @param saveToFile Boolean If set to true it will immediately encodes the grab to a PNG and open a "Save As" dialog window when the hotkey is pressed
* @param hideMouse Boolean If set to true the mouse will be hidden before capture and displayed afterwards when the hotkey is pressed
*/
public static function defineHotKey(key:String, saveToFile:Boolean = false, hideMouse:Boolean = false):void
{
hotkey = key;
autoSave = saveToFile;
autoHideMouse = hideMouse;
}
/**
* Clears a previously defined hotkey
*/
public static function clearHotKey():void
{
hotkey = "";
autoSave = false;
autoHideMouse = false;
}
/**
* Takes a screen grab immediately of the given region or a previously defined region
*
* @param captureRegion A Rectangle area to capture. This over-rides that set by "defineCaptureRegion". If neither are set the full SWF size is used.
* @param saveToFile Boolean If set to true it will immediately encode the grab to a PNG and open a "Save As" dialog window
* @param hideMouse Boolean If set to true the mouse will be hidden before capture and displayed again afterwards
* @return Bitmap The screen grab as a Flash Bitmap image
*/
public static function grab(captureRegion:Rectangle = null, saveToFile:Boolean = false, hideMouse:Boolean = false):Bitmap
{
var bounds:Rectangle;
if (captureRegion)
{
bounds = new Rectangle(captureRegion.x, captureRegion.y, captureRegion.width, captureRegion.height);
}
else if (region)
{
bounds = new Rectangle(region.x, region.y, region.width, region.height);
}
else
{
bounds = new Rectangle(0, 0, FlxG.stage.stageWidth, FlxG.stage.stageHeight);
}
var theBitmap:Bitmap = new Bitmap(new BitmapData(bounds.width, bounds.height, true, 0x0));
var m:Matrix = new Matrix(1, 0, 0, 1, -bounds.x, -bounds.y);
if (autoHideMouse || hideMouse)
{
FlxG.mouse.hide();
}
theBitmap.bitmapData.draw(FlxG.stage, m);
if (autoHideMouse || hideMouse)
{
FlxG.mouse.show();
}
screenshot = theBitmap;
if (saveToFile || autoSave)
{
save();
}
return theBitmap;
}
private static function save(filename:String = ""):void
{
if (screenshot.bitmapData == null)
{
return;
}
var png:ByteArray = PNGEncoder.encode(screenshot.bitmapData);
var file:FileReference = new FileReference();
if (filename == "")
{
filename = "grab" + getTimer().toString() + ".png";
}
else if (filename.substr( -4) != ".png")
{
filename = filename.concat(".png");
}
file.save(png, filename);
}
override public function update():void
{
if (hotkey != "")
{
if (FlxG.keys.justReleased(hotkey))
{
trace("key pressed");
grab();
}
}
}
override public function destroy():void
{
clearCaptureRegion();
clearHotKey();
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxScrollZone.as
================================================
/**
* FlxScrollZone
* -- Part of the Flixel Power Tools set
*
* v1.4 Added "clearRegion" support for when you use Sprites with transparency and renamed parameter to onlyScrollOnscreen
* v1.3 Swapped plugin update for draw, now smoother / faster in some fps cases
* v1.2 Updated for the Flixel 2.5 Plugin system
*
* @version 1.4 - May 16th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm. My thanks to Ralph Hauwert for help with this.
*/
package org.flixel.plugin.photonstorm
{
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import org.flixel.*;
/**
* FlxScrollZone allows you to scroll the content of an FlxSprites bitmapData in any direction you like.
*/
public class FlxScrollZone extends FlxBasic
{
private static var members:Dictionary = new Dictionary(true);
private static var zeroPoint:Point = new Point;
public function FlxScrollZone()
{
}
/**
* Add an FlxSprite to the Scroll Manager, setting up one scrolling region.
* To add extra scrolling regions on the same sprite use addZone()
*
* @param source The FlxSprite to apply the scroll to
* @param region The region, specified as a Rectangle, of the FlxSprite that you wish to scroll
* @param distanceX The distance in pixels you want to scroll on the X axis. Negative values scroll left. Positive scroll right. Floats allowed (0.5 would scroll at half speed)
* @param distanceY The distance in pixels you want to scroll on the Y axis. Negative values scroll up. Positive scroll down. Floats allowed (0.5 would scroll at half speed)
* @param onlyScrollOnscreen Only update this FlxSprite if visible onScreen (default true) Saves performance by not scrolling offscreen sprites, but this isn't always desirable
* @param clearRegion Set to true if you want to clear the scrolling area of the FlxSprite with a 100% transparent fill before applying the scroll texture (default false)
* @see createZone
*/
public static function add(source:FlxSprite, region:Rectangle, distanceX:Number, distanceY:Number, onlyScrollOnscreen:Boolean = true, clearRegion:Boolean = false):void
{
if (members[source])
{
throw Error("FlxSprite already exists in FlxScrollZone, use addZone to add a new scrolling region to an already added FlxSprite");
}
var data:Object = new Object();
data.source = source;
data.scrolling = true;
data.onlyScrollOnscreen = onlyScrollOnscreen;
data.zones = new Array;
members[source] = data;
createZone(source, region, distanceX, distanceY, clearRegion);
}
/**
* Creates a new scrolling region to an FlxSprite already in the Scroll Manager (see add())
*
* @param source The FlxSprite to apply the scroll to
* @param region The region, specified as a Rectangle, of the FlxSprite that you wish to scroll
* @param distanceX The distance in pixels you want to scroll on the X axis. Negative values scroll left. Positive scroll right. Floats allowed (0.5 would scroll at half speed)
* @param distanceY The distance in pixels you want to scroll on the Y axis. Negative values scroll up. Positive scroll down. Floats allowed (0.5 would scroll at half speed)
* @param clearRegion Set to true if you want to fill the scroll region of the FlxSprite with a 100% transparent fill before scrolling it (default false)
*/
public static function createZone(source:FlxSprite, region:Rectangle, distanceX:Number, distanceY:Number, clearRegion:Boolean = false):void
{
var texture:BitmapData = new BitmapData(region.width, region.height, true, 0x00000000);
texture.copyPixels(source.framePixels, region, zeroPoint, null, null, true);
var data:Object = new Object();
data.buffer = new Sprite;
data.texture = texture;
data.region = region;
data.clearRegion = clearRegion;
data.distanceX = distanceX;
data.distanceY = distanceY;
data.scrollMatrix = new Matrix();
data.drawMatrix = new Matrix(1, 0, 0, 1, region.x, region.y);
members[source].zones.push(data);
}
/**
* Sets the draw Matrix for the given FlxSprite scroll zone
* Warning: Modify this at your own risk!
*
* @param source The FlxSprite to set the draw matrix on
* @param matrix The Matrix to use during the scroll update draw
* @param zone If the FlxSprite has more than 1 scrolling zone, use this to target which zone to apply the update to (default 0)
* @return Matrix The draw matrix used in the scroll update
*/
public static function updateDrawMatrix(source:FlxSprite, matrix:Matrix, zone:int = 0):void
{
members[source].zones[zone].drawMatrix = matrix;
}
/**
* Returns the draw Matrix for the given FlxSprite scroll zone
*
* @param source The FlxSprite to get the draw matrix from
* @param zone If the FlxSprite has more than 1 scrolling zone, use this to target which zone to apply the update to (default 0)
* @return Matrix The draw matrix used in the scroll update
*/
public static function getDrawMatrix(source:FlxSprite, zone:int = 0):Matrix
{
return members[source].zones[zone].drawMatrix;
}
/**
* Removes an FlxSprite and all of its scrolling zones. Note that it doesn't restore the sprite bitmapData.
*
* @param source The FlxSprite to remove all scrolling zones for.
* @return Boolean true if the FlxSprite was removed, otherwise false.
*/
public static function remove(source:FlxSprite):Boolean
{
if (members[source])
{
delete members[source];
return true;
}
return false;
}
/**
* Removes all FlxSprites, and all of their scrolling zones.
* This is called automatically if the plugin is ever destroyed.
*/
public static function clear():void
{
for each (var obj:Object in members)
{
delete members[obj.source];
}
}
/**
* Update the distance in pixels to scroll on the X axis.
*
* @param source The FlxSprite to apply the scroll to
* @param distanceX The distance in pixels you want to scroll on the X axis. Negative values scroll left. Positive scroll right. Floats allowed (0.5 would scroll at half speed)
* @param zone If the FlxSprite has more than 1 scrolling zone, use this to target which zone to apply the update to (default 0)
*/
public static function updateX(source:FlxSprite, distanceX:Number, zone:int = 0):void
{
members[source].zones[zone].distanceX = distanceX;
}
/**
* Update the distance in pixels to scroll on the X axis.
*
* @param source The FlxSprite to apply the scroll to
* @param distanceY The distance in pixels you want to scroll on the Y axis. Negative values scroll up. Positive scroll down. Floats allowed (0.5 would scroll at half speed)
* @param zone If the FlxSprite has more than 1 scrolling zone, use this to target which zone to apply the update to (default 0)
*/
public static function updateY(source:FlxSprite, distanceY:Number, zone:int = 0):void
{
members[source].zones[zone].distanceY = distanceY;
}
/**
* Starts scrolling on the given FlxSprite. If no FlxSprite is given it starts scrolling on all FlxSprites currently added.
* Scrolling is enabled by default, but this can be used to re-start it if you have stopped it via stopScrolling.
*
* @param source The FlxSprite to start scrolling on. If left as null it will start scrolling on all sprites.
*/
public static function startScrolling(source:FlxSprite = null):void
{
if (source)
{
members[source].scrolling = true;
}
else
{
for each (var obj:Object in members)
{
obj.scrolling = true;
}
}
}
/**
* Stops scrolling on the given FlxSprite. If no FlxSprite is given it stops scrolling on all FlxSprites currently added.
* Scrolling is enabled by default, but this can be used to stop it.
*
* @param source The FlxSprite to stop scrolling on. If left as null it will stop scrolling on all sprites.
*/
public static function stopScrolling(source:FlxSprite = null):void
{
if (source)
{
members[source].scrolling = false;
}
else
{
for each (var obj:Object in members)
{
obj.scrolling = false;
}
}
}
override public function draw():void
{
for each (var obj:Object in members)
{
if ((obj.onlyScrollOnscreen == true && obj.source.onScreen()) && obj.scrolling == true && obj.source.exists)
{
scroll(obj);
}
}
}
private function scroll(data:Object):void
{
// Loop through the scroll zones defined in this object
for each (var zone:Object in data.zones)
{
zone.scrollMatrix.tx += zone.distanceX;
zone.scrollMatrix.ty += zone.distanceY;
zone.buffer.graphics.clear();
zone.buffer.graphics.beginBitmapFill(zone.texture, zone.scrollMatrix, true, false);
zone.buffer.graphics.drawRect(0, 0, zone.region.width, zone.region.height);
zone.buffer.graphics.endFill();
if (zone.clearRegion)
{
data.source.pixels.fillRect(zone.region, 0x0);
}
data.source.pixels.draw(zone.buffer, zone.drawMatrix);
}
data.source.dirty = true;
}
override public function destroy():void
{
clear();
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxScrollingText.as
================================================
/**
* FlxScrollingText
* -- Part of the Flixel Power Tools set
*
* v1.0 First version released
*
* @version 1.0 - May 5th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
package org.flixel.plugin.photonstorm
{
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.Dictionary;
import flash.utils.getTimer;
import org.flixel.*;
/**
* FlxScrollingText takes an FlxBitmapFont object and creates a horizontally scrolling FlxSprite from it
*/
public class FlxScrollingText extends FlxBasic
{
private static var members:Dictionary = new Dictionary(true);
private static var zeroPoint:Point = new Point;
public function FlxScrollingText()
{
}
/**
* Adds an FlxBitmapFont to the Scrolling Text Manager and returns an FlxSprite which contains the text scroller in it.
* The FlxSprite will automatically update itself via this plugin, but can be treated as a normal FlxSprite in all other regards
* re: positioning, collision, rotation, etc.
*
* @param bitmapFont A pre-prepared FlxBitmapFont object (see the Test Suite examples for details on how this works)
* @param region A Rectangle that defines the size of the scrolling FlxSprite. The sprite will be placed at region.x/y and be region.width/height in size.
* @param pixels The number of pixels to scroll per step. For a smooth (but slow) scroll use low values. Keep the value proportional to the font width, so if the font width is 16 use a value like 1, 2, 4 or 8.
* @param steps How many steps should pass before the text is next scrolled? Default 0 means every step we scroll. Higher values slow things down.
* @param text The default text for your scrolling message. Can be changed in real-time via the addText method.
* @param onlyScrollOnscreen Only update the text scroller when this FlxSprite is visible on-screen? Default true.
* @param loopOnWrap When the scroller reaches the end of the given "text" should it wrap to the start? Default true. If false it will clear the screen then set itself to not update.
*
* @return An FlxSprite of size region.width/height, positioned at region.x/y, that auto-updates its contents while this plugin runs
*/
public static function add(bitmapFont:FlxBitmapFont, region:Rectangle, pixels:uint = 1, steps:uint = 0, text:String = "FLIXEL ROCKS!", onlyScrollOnscreen:Boolean = true, loopOnWrap:Boolean = true):FlxSprite
{
var data:Object = new Object;
// Sanity checks
if (pixels > bitmapFont.characterWidth)
{
pixels = bitmapFont.characterWidth;
}
if (pixels == 0)
{
pixels = 1;
}
if (text == "")
{
text = " ";
}
data.bitmapFont = bitmapFont;
data.bitmapChar = FlxBitmapFont(data.bitmapFont).getCharacterAsBitmapData(text.charAt(0));
data.charWidth = bitmapFont.characterWidth;
data.charHeight = bitmapFont.characterHeight;
data.shiftRect = new Rectangle(pixels, 0, region.width - pixels, region.height);
data.bufferRect = new Rectangle(0, 0, region.width, region.height);
data.slice = new Rectangle(0, 0, pixels, data.charHeight);
data.endPoint = new Point(region.width - pixels, 0);
data.x = 0;
data.sprite = new FlxSprite(region.x, region.y).makeGraphic(region.width, region.height, 0x0, true);
data.buffer = new BitmapData(region.width, region.height, true, 0x0);
data.region = region;
data.step = steps;
data.maxStep = steps;
data.pixels = pixels;
data.clearCount = 0;
data.clearDistance = region.width - pixels;
data.text = text;
data.currentChar = 0;
data.maxChar = text.length;
data.wrap = loopOnWrap;
data.complete = false;
data.scrolling = true;
data.onScreenScroller = onlyScrollOnscreen;
scroll(data);
members[data.sprite] = data;
return data.sprite;
}
/**
* Adds or replaces the text in the given Text Scroller.
* Can be called while the scroller is still active.
*
* @param source The FlxSprite Text Scroller you wish to update (must have been added to FlxScrollingText via a call to add()
* @param text The text to add or update to the Scroller
* @param overwrite If true the given text will fully replace the previous scroller text. If false it will be appended to the end (default)
*/
public static function addText(source:FlxSprite, text:String, overwrite:Boolean = false):void
{
if (overwrite)
{
members[source].text = text;
}
else
{
members[source].text = String(members[source].text).concat(text);
}
members[source].maxChar = members[source].text.length;
}
override public function draw():void
{
for each (var obj:Object in members)
{
if (obj && (obj.onScreenScroller == true && obj.sprite.onScreen()) && obj.scrolling == true && obj.sprite.exists)
{
scroll(obj);
}
}
}
private static function scroll(data:Object):void
{
// Have we reached enough steps?
if (data.maxStep > 0 && (data.step < data.maxStep))
{
data.step++;
return;
}
else
{
// It's time to render, so reset the step counter and lets go
data.step = 0;
}
// CLS
data.buffer.fillRect(data.bufferRect, 0x0);
// Shift the current contents of the buffer along by "speed" pixels
data.buffer.copyPixels(data.sprite.pixels, data.shiftRect, zeroPoint, null, null, true);
// Copy the side of the character
if (data.complete == false)
{
data.buffer.copyPixels(data.bitmapChar, data.slice, data.endPoint, null, null, true);
// Update
data.x += data.pixels;
if (data.x >= data.charWidth)
{
// Get the next character
data.currentChar++;
if (data.currentChar > data.maxChar)
{
// At the end of the text
if (data.wrap)
{
data.currentChar = 0;
}
else
{
data.complete = true;
data.clearCount = 0;
}
}
if (data.complete == false)
{
data.bitmapChar = FlxBitmapFont(data.bitmapFont).getCharacterAsBitmapData(String(data.text).charAt(data.currentChar));
data.x = 0;
}
}
if (data.complete == false)
{
data.slice.x = data.x;
}
}
else
{
data.clearCount += data.pixels;
// It's all over now
if (data.clearCount >= data.clearDistance)
{
// No point updating something that has since left the screen
data.scrolling = false;
}
}
data.sprite.pixels = data.buffer.clone();
data.sprite.dirty = true;
}
/**
* Removes all FlxSprites
* This is called automatically if the plugin is destroyed, but should be called manually by you if you change States
* as all the FlxSprites will be destroyed by Flixel otherwise
*/
public static function clear():void
{
for each (var obj:Object in members)
{
delete members[obj.sprite];
}
}
/**
* Starts scrolling on the given FlxSprite. If no FlxSprite is given it starts scrolling on all FlxSprites currently added.
* Scrolling is enabled by default, but this can be used to re-start it if you have stopped it via stopScrolling.
*
* @param source The FlxSprite to start scrolling on. If left as null it will start scrolling on all sprites.
*/
public static function startScrolling(source:FlxSprite = null):void
{
if (source)
{
members[source].scrolling = true;
}
else
{
for each (var obj:Object in members)
{
obj.scrolling = true;
}
}
}
/**
* Stops scrolling on the given FlxSprite. If no FlxSprite is given it stops scrolling on all FlxSprites currently added.
* Scrolling is enabled by default, but this can be used to stop it.
*
* @param source The FlxSprite to stop scrolling on. If left as null it will stop scrolling on all sprites.
*/
public static function stopScrolling(source:FlxSprite = null):void
{
if (source)
{
members[source].scrolling = false;
}
else
{
for each (var obj:Object in members)
{
obj.scrolling = false;
}
}
}
/**
* Checks to see if the given FlxSprite is a Scrolling Text, and is actively scrolling or not
* Note: If the text is set to only scroll when on-screen, but if off-screen when this is called, it will still return true.
*
* @param source The FlxSprite to check for scrolling on.
* @return Boolean true is the FlxSprite was found and is scrolling, otherwise false
*/
public static function isScrolling(source:FlxSprite):Boolean
{
if (members[source])
{
return members[source].scrolling;
}
return false;
}
/**
* Removes an FlxSprite from the Text Scroller. Note that it doesn't restore the sprite bitmapData.
*
* @param source The FlxSprite to remove scrolling for.
* @return Boolean true if the FlxSprite was removed, otherwise false.
*/
public static function remove(source:FlxSprite):Boolean
{
if (members[source])
{
delete members[source];
return true;
}
return false;
}
override public function destroy():void
{
clear();
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxSpecialFX.as
================================================
/**
* FlxSpecialFX
* -- Part of the Flixel Power Tools set
*
* v1.6 Added WowCopperFX
* v1.5 Added RevealFX
* v1.4 Added BlurFX and CenterSlideFX
* v1.3 Renamed DropDown to FloodFill
* v1.2 Added GlitchFX and StarfieldFX
* v1.1 Added SineWaveFX
* v1.0 First release of the new FlxSpecialFX system
*
* @version 1.6 - September 19th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
package org.flixel.plugin.photonstorm
{
import flash.utils.Dictionary;
import org.flixel.*;
import org.flixel.plugin.photonstorm.FX.BlurFX;
import org.flixel.plugin.photonstorm.FX.CenterSlideFX;
import org.flixel.plugin.photonstorm.FX.FloodFillFX;
import org.flixel.plugin.photonstorm.FX.GlitchFX;
import org.flixel.plugin.photonstorm.FX.PlasmaFX;
import org.flixel.plugin.photonstorm.FX.RainbowLineFX;
import org.flixel.plugin.photonstorm.FX.RevealFX;
import org.flixel.plugin.photonstorm.FX.SineWaveFX;
import org.flixel.plugin.photonstorm.FX.StarfieldFX;
import org.flixel.plugin.photonstorm.FX.WowCopperFX;
/**
* FlxSpecialFX is a single point of access to all of the FX Plugins available in the Flixel Power Tools
*/
public class FlxSpecialFX extends FlxBasic
{
private static var members:Dictionary = new Dictionary(true);
public function FlxSpecialFX()
{
}
// THE SPECIAL FX PLUGINS AVAILABLE
/**
* Creates a Plama field Effect
*
* @return PlasmaFX
*/
public static function plasma():PlasmaFX
{
var temp:PlasmaFX = new PlasmaFX;
members[temp] = temp;
return members[temp];
}
/**
* Creates a Rainbow Line Effect
*
* @return RainbowLineFX
*/
public static function rainbowLine():RainbowLineFX
{
var temp:RainbowLineFX = new RainbowLineFX;
members[temp] = temp;
return members[temp];
}
/**
* Creates a Flood Fill Effect
*
* @return FloodFillFX
*/
public static function floodFill():FloodFillFX
{
var temp:FloodFillFX = new FloodFillFX;
members[temp] = temp;
return members[temp];
}
/**
* Creates a Sine Wave Down Effect
*
* @return SineWaveFX
*/
public static function sineWave():SineWaveFX
{
var temp:SineWaveFX = new SineWaveFX;
members[temp] = temp;
return members[temp];
}
/**
* Creates a Glitch Effect
*
* @return GlitchFX
*/
public static function glitch():GlitchFX
{
var temp:GlitchFX = new GlitchFX;
members[temp] = temp;
return members[temp];
}
/**
* Creates a 2D or 3D Starfield Effect
*
* @return StarfieldFX
*/
public static function starfield():StarfieldFX
{
var temp:StarfieldFX = new StarfieldFX;
members[temp] = temp;
return members[temp];
}
/**
* Creates a Blur Effect
*
* @return BlurFX
*/
public static function blur():BlurFX
{
var temp:BlurFX = new BlurFX;
members[temp] = temp;
return members[temp];
}
/**
* Creates a Center Slide Effect
*
* @return CenterSlideFX
*/
public static function centerSlide():CenterSlideFX
{
var temp:CenterSlideFX = new CenterSlideFX
members[temp] = temp;
return members[temp];
}
/**
* Creates a Reveal Effect
*
* @return RevealFX
*/
public static function reveal():RevealFX
{
var temp:RevealFX = new RevealFX
members[temp] = temp;
return members[temp];
}
/**
* Creates a WOW Copper Effect
*
* @return WowCopperFX
*/
public static function wowCopper():WowCopperFX
{
var temp:WowCopperFX = new WowCopperFX
members[temp] = temp;
return members[temp];
}
// GLOBAL FUNCTIONS
/**
* Starts the given FX Plugin running
*
* @param source A reference to the FX Plugin you wish to run. If null it will start all currently added FX Plugins
*/
public static function startFX(source:Class = null):void
{
if (source)
{
members[source].active = true;
}
else
{
for each (var obj:Object in members)
{
obj.active = true;
}
}
}
/**
* Stops the given FX Plugin running
*
* @param source A reference to the FX Plugin you wish to stop. If null it will stop all currently added FX Plugins
*/
public static function stopFX(source:Class = null):void
{
if (source)
{
members[source].active = false;
}
else
{
for each (var obj:Object in members)
{
obj.active = false;
}
}
}
/**
* Returns the active state of the given FX Plugin running
*
* @param source A reference to the FX Plugin you wish to run. If null it will start all currently added FX Plugins
* @return Boolean true if the FX Plugin is active, false if not
*/
public static function isActive(source:Class):Boolean
{
if (members[source])
{
return members[source].active;
}
return false;
}
/**
* Called automatically by Flixels Plugin handler
*/
override public function draw():void
{
if (FlxG.paused)
{
return;
}
for each (var obj:Object in members)
{
if (obj.active)
{
obj.draw();
}
}
}
/**
* Removes a FX Plugin from the Special FX Handler
*
* @param source The FX Plugin to remove
* @return Boolean true if the plugin was removed, otherwise false.
*/
public static function remove(source:Object):Boolean
{
if (members[source])
{
members[source].destroy();
delete members[source];
return true;
}
return false;
}
/**
* Removes all FX Plugins
* This is called automatically if the plugin is destroyed, but should be called manually by you if you change States
*/
public static function clear():void
{
for each (var obj:Object in members)
{
remove(obj);
}
}
/**
* Destroys all FX Plugins currently added and then destroys this instance of the FlxSpecialFX Plugin
*/
override public function destroy():void
{
clear();
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxVelocity.as
================================================
/**
* FlxVelocity
* -- Part of the Flixel Power Tools set
*
* v1.6 New method: velocityFromFacing
* v1.5 New methods: velocityFromAngle, accelerateTowardsObject, accelerateTowardsMouse, accelerateTowardsPoint
* v1.4 New methods: moveTowardsPoint, distanceToPoint, angleBetweenPoint
* v1.3 Updated for the Flixel 2.5 Plugin system
*
* @version 1.6 - August 15th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
* @see Depends on FlxMath
*/
package org.flixel.plugin.photonstorm
{
import flash.accessibility.Accessibility;
import org.flixel.*;
public class FlxVelocity
{
public function FlxVelocity()
{
}
/**
* Sets the source FlxSprite x/y velocity so it will move directly towards the destination FlxSprite at the speed given (in pixels per second)
* If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
* Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.
* The source object doesn't stop moving automatically should it ever reach the destination coordinates.
* If you need the object to accelerate, see accelerateTowardsObject() instead
* Note: Doesn't take into account acceleration, maxVelocity or drag (if you set drag or acceleration too high this object may not move at all)
*
* @param source The FlxSprite on which the velocity will be set
* @param dest The FlxSprite where the source object will move to
* @param speed The speed it will move, in pixels per second (default is 60 pixels/sec)
* @param maxTime Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the source will arrive at destination in the given number of ms
*/
public static function moveTowardsObject(source:FlxSprite, dest:FlxSprite, speed:int = 60, maxTime:int = 0):void
{
var a:Number = angleBetween(source, dest);
if (maxTime > 0)
{
var d:int = distanceBetween(source, dest);
// We know how many pixels we need to move, but how fast?
speed = d / (maxTime / 1000);
}
source.velocity.x = Math.cos(a) * speed;
source.velocity.y = Math.sin(a) * speed;
}
/**
* Sets the x/y acceleration on the source FlxSprite so it will move towards the destination FlxSprite at the speed given (in pixels per second)
* You must give a maximum speed value, beyond which the FlxSprite won't go any faster.
* If you don't need acceleration look at moveTowardsObject() instead.
*
* @param source The FlxSprite on which the acceleration will be set
* @param dest The FlxSprite where the source object will move towards
* @param speed The speed it will accelerate in pixels per second
* @param xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally
* @param ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically
*/
public static function accelerateTowardsObject(source:FlxSprite, dest:FlxSprite, speed:int, xSpeedMax:uint, ySpeedMax:uint):void
{
var a:Number = angleBetween(source, dest);
source.velocity.x = 0;
source.velocity.y = 0;
source.acceleration.x = int(Math.cos(a) * speed);
source.acceleration.y = int(Math.sin(a) * speed);
source.maxVelocity.x = xSpeedMax;
source.maxVelocity.y = ySpeedMax;
}
/**
* Move the given FlxSprite towards the mouse pointer coordinates at a steady velocity
* If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
* Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.
* The source object doesn't stop moving automatically should it ever reach the destination coordinates.
*
* @param source The FlxSprite to move
* @param speed The speed it will move, in pixels per second (default is 60 pixels/sec)
* @param maxTime Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the source will arrive at destination in the given number of ms
*/
public static function moveTowardsMouse(source:FlxSprite, speed:int = 60, maxTime:int = 0):void
{
var a:Number = angleBetweenMouse(source);
if (maxTime > 0)
{
var d:int = distanceToMouse(source);
// We know how many pixels we need to move, but how fast?
speed = d / (maxTime / 1000);
}
source.velocity.x = Math.cos(a) * speed;
source.velocity.y = Math.sin(a) * speed;
}
/**
* Sets the x/y acceleration on the source FlxSprite so it will move towards the mouse coordinates at the speed given (in pixels per second)
* You must give a maximum speed value, beyond which the FlxSprite won't go any faster.
* If you don't need acceleration look at moveTowardsMouse() instead.
*
* @param source The FlxSprite on which the acceleration will be set
* @param speed The speed it will accelerate in pixels per second
* @param xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally
* @param ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically
*/
public static function accelerateTowardsMouse(source:FlxSprite, speed:int, xSpeedMax:uint, ySpeedMax:uint):void
{
var a:Number = angleBetweenMouse(source);
source.velocity.x = 0;
source.velocity.y = 0;
source.acceleration.x = int(Math.cos(a) * speed);
source.acceleration.y = int(Math.sin(a) * speed);
source.maxVelocity.x = xSpeedMax;
source.maxVelocity.y = ySpeedMax;
}
/**
* Sets the x/y velocity on the source FlxSprite so it will move towards the target coordinates at the speed given (in pixels per second)
* If you specify a maxTime then it will adjust the speed (over-writing what you set) so it arrives at the destination in that number of seconds.
* Timings are approximate due to the way Flash timers work, and irrespective of SWF frame rate. Allow for a variance of +- 50ms.
* The source object doesn't stop moving automatically should it ever reach the destination coordinates.
*
* @param source The FlxSprite to move
* @param target The FlxPoint coordinates to move the source FlxSprite towards
* @param speed The speed it will move, in pixels per second (default is 60 pixels/sec)
* @param maxTime Time given in milliseconds (1000 = 1 sec). If set the speed is adjusted so the source will arrive at destination in the given number of ms
*/
public static function moveTowardsPoint(source:FlxSprite, target:FlxPoint, speed:int = 60, maxTime:int = 0):void
{
var a:Number = angleBetweenPoint(source, target);
if (maxTime > 0)
{
var d:int = distanceToPoint(source, target);
// We know how many pixels we need to move, but how fast?
speed = d / (maxTime / 1000);
}
source.velocity.x = Math.cos(a) * speed;
source.velocity.y = Math.sin(a) * speed;
}
/**
* Sets the x/y acceleration on the source FlxSprite so it will move towards the target coordinates at the speed given (in pixels per second)
* You must give a maximum speed value, beyond which the FlxSprite won't go any faster.
* If you don't need acceleration look at moveTowardsPoint() instead.
*
* @param source The FlxSprite on which the acceleration will be set
* @param target The FlxPoint coordinates to move the source FlxSprite towards
* @param speed The speed it will accelerate in pixels per second
* @param xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally
* @param ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically
*/
public static function accelerateTowardsPoint(source:FlxSprite, target:FlxPoint, speed:int, xSpeedMax:uint, ySpeedMax:uint):void
{
var a:Number = angleBetweenPoint(source, target);
source.velocity.x = 0;
source.velocity.y = 0;
source.acceleration.x = int(Math.cos(a) * speed);
source.acceleration.y = int(Math.sin(a) * speed);
source.maxVelocity.x = xSpeedMax;
source.maxVelocity.y = ySpeedMax;
}
/**
* Find the distance (in pixels, rounded) between two FlxSprites, taking their origin into account
*
* @param a The first FlxSprite
* @param b The second FlxSprite
* @return int Distance (in pixels)
*/
public static function distanceBetween(a:FlxSprite, b:FlxSprite):int
{
var dx:Number = (a.x + a.origin.x) - (b.x + b.origin.x);
var dy:Number = (a.y + a.origin.y) - (b.y + b.origin.y);
return int(FlxMath.vectorLength(dx, dy));
}
/**
* Find the distance (in pixels, rounded) from an FlxSprite to the given FlxPoint, taking the source origin into account
*
* @param a The first FlxSprite
* @param target The FlxPoint
* @return int Distance (in pixels)
*/
public static function distanceToPoint(a:FlxSprite, target:FlxPoint):int
{
var dx:Number = (a.x + a.origin.x) - (target.x);
var dy:Number = (a.y + a.origin.y) - (target.y);
return int(FlxMath.vectorLength(dx, dy));
}
/**
* Find the distance (in pixels, rounded) from the object x/y and the mouse x/y
*
* @param a The FlxSprite to test against
* @return int The distance between the given sprite and the mouse coordinates
*/
public static function distanceToMouse(a:FlxSprite):int
{
var dx:Number = (a.x + a.origin.x) - FlxG.mouse.screenX;
var dy:Number = (a.y + a.origin.y) - FlxG.mouse.screenY;
return int(FlxMath.vectorLength(dx, dy));
}
/**
* Find the angle (in radians) between an FlxSprite and an FlxPoint. The source sprite takes its x/y and origin into account.
* The angle is calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative)
*
* @param a The FlxSprite to test from
* @param target The FlxPoint to angle the FlxSprite towards
* @param asDegrees If you need the value in degrees instead of radians, set to true
*
* @return Number The angle (in radians unless asDegrees is true)
*/
public static function angleBetweenPoint(a:FlxSprite, target:FlxPoint, asDegrees:Boolean = false):Number
{
var dx:Number = (target.x) - (a.x + a.origin.x);
var dy:Number = (target.y) - (a.y + a.origin.y);
if (asDegrees)
{
return FlxMath.asDegrees(Math.atan2(dy, dx));
}
else
{
return Math.atan2(dy, dx);
}
}
/**
* Find the angle (in radians) between the two FlxSprite, taking their x/y and origin into account.
* The angle is calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative)
*
* @param a The FlxSprite to test from
* @param b The FlxSprite to test to
* @param asDegrees If you need the value in degrees instead of radians, set to true
*
* @return Number The angle (in radians unless asDegrees is true)
*/
public static function angleBetween(a:FlxSprite, b:FlxSprite, asDegrees:Boolean = false):Number
{
var dx:Number = (b.x + b.origin.x) - (a.x + a.origin.x);
var dy:Number = (b.y + b.origin.y) - (a.y + a.origin.y);
if (asDegrees)
{
return FlxMath.asDegrees(Math.atan2(dy, dx));
}
else
{
return Math.atan2(dy, dx);
}
}
/**
* Given the angle and speed calculate the velocity and return it as an FlxPoint
*
* @param angle The angle (in degrees) calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative)
* @param speed The speed it will move, in pixels per second sq
*
* @return An FlxPoint where FlxPoint.x contains the velocity x value and FlxPoint.y contains the velocity y value
*/
public static function velocityFromAngle(angle:int, speed:int):FlxPoint
{
var a:Number = FlxMath.asRadians(angle);
var result:FlxPoint = new FlxPoint;
result.x = int(Math.cos(a) * speed);
result.y = int(Math.sin(a) * speed);
return result;
}
/**
* Given the FlxSprite and speed calculate the velocity and return it as an FlxPoint based on the direction the sprite is facing
*
* @param parent The FlxSprite to get the facing value from
* @param speed The speed it will move, in pixels per second sq
*
* @return An FlxPoint where FlxPoint.x contains the velocity x value and FlxPoint.y contains the velocity y value
*/
public static function velocityFromFacing(parent:FlxSprite, speed:int):FlxPoint
{
var a:Number;
if (parent.facing == FlxObject.LEFT)
{
a = FlxMath.asRadians(180);
}
else if (parent.facing == FlxObject.RIGHT)
{
a = FlxMath.asRadians(0);
}
else if (parent.facing == FlxObject.UP)
{
a = FlxMath.asRadians( -90);
}
else if (parent.facing == FlxObject.DOWN)
{
a = FlxMath.asRadians(90);
}
var result:FlxPoint = new FlxPoint;
result.x = int(Math.cos(a) * speed);
result.y = int(Math.sin(a) * speed);
return result;
}
/**
* Find the angle (in radians) between an FlxSprite and the mouse, taking their x/y and origin into account.
* The angle is calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative)
*
* @param a The FlxObject to test from
* @param asDegrees If you need the value in degrees instead of radians, set to true
*
* @return Number The angle (in radians unless asDegrees is true)
*/
public static function angleBetweenMouse(a:FlxSprite, asDegrees:Boolean = false):Number
{
// In order to get the angle between the object and mouse, we need the objects screen coordinates (rather than world coordinates)
var p:FlxPoint = a.getScreenXY();
var dx:Number = FlxG.mouse.screenX - p.x;
var dy:Number = FlxG.mouse.screenY - p.y;
if (asDegrees)
{
return FlxMath.asDegrees(Math.atan2(dy, dx));
}
else
{
return Math.atan2(dy, dx);
}
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/FlxWeapon.as
================================================
/**
* FlxWeapon
* -- Part of the Flixel Power Tools set
*
* v1.3 Added bullet elasticity and bulletsFired counter
* v1.2 Added useParentDirection boolean
* v1.1 Added pre-fire, fire and post-fire callbacks and sound support, rnd factors, boolean returns and currentBullet
* v1.0 First release
*
* @version 1.3 - October 9th 2011
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
package org.flixel.plugin.photonstorm
{
import org.flixel.*;
import flash.utils.getTimer;
import org.flixel.plugin.photonstorm.BaseTypes.Bullet;
import org.flixel.plugin.photonstorm.FlxVelocity;
/**
* TODO
* ----
*
* Angled bullets
* Baked Rotation support for angled bullets
* Bullet death styles (particle effects)
* Bullet trails - blur FX style and Missile Command "draw lines" style? (could be another FX plugin)
* Homing Missiles
* Bullet uses random sprite from sprite sheet (for rainbow style bullets), or cycles through them in sequence?
* Some Weapon base classes like shotgun, lazer, etc?
*/
public class FlxWeapon
{
/**
* Internal name for this weapon (i.e. "pulse rifle")
*/
public var name:String;
/**
* The FlxGroup into which all the bullets for this weapon are drawn. This should be added to your display and collision checked against it.
*/
public var group:FlxGroup;
// Bullet values
public var bounds:FlxRect;
private var bulletSpeed:uint;
private var rotateToAngle:Boolean;
// When firing from a fixed position (i.e. Missile Command)
private var fireFromPosition:Boolean;
private var fireX:int;
private var fireY:int;
private var lastFired:uint = 0;
private var nextFire:uint = 0;
private var fireRate:uint = 0;
// When firing from a parent sprites position (i.e. Space Invaders)
private var fireFromParent:Boolean;
private var parent:*;
private var parentXVariable:String;
private var parentYVariable:String;
private var positionOffset:FlxPoint;
private var directionFromParent:Boolean;
private var angleFromParent:Boolean;
private var velocity:FlxPoint;
public var multiShot:uint = 0;
public var bulletLifeSpan:uint = 0;
public var bulletElasticity:Number = 0;
public var rndFactorAngle:uint = 0;
public var rndFactorLifeSpan:uint = 0;
public var rndFactorSpeed:uint = 0;
public var rndFactorPosition:FlxPoint = new FlxPoint;
/**
* A reference to the Bullet that was fired
*/
public var currentBullet:Bullet;
// Callbacks
public var onPreFireCallback:Function;
public var onFireCallback:Function;
public var onPostFireCallback:Function;
// Sounds
public var onPreFireSound:FlxSound;
public var onFireSound:FlxSound;
public var onPostFireSound:FlxSound;
// Quick firing direction angle constants
public static const BULLET_UP:int = -90;
public static const BULLET_DOWN:int = 90;
public static const BULLET_LEFT:int = 180;
public static const BULLET_RIGHT:int = 0;
public static const BULLET_NORTH_EAST:int = -45;
public static const BULLET_NORTH_WEST:int = -135;
public static const BULLET_SOUTH_EAST:int = 45;
public static const BULLET_SOUTH_WEST:int = 135;
/**
* Keeps a tally of how many bullets have been fired by this weapon
*/
public var bulletsFired:uint = 0;
// TODO :)
private var currentMagazine:uint;
//private var currentBullet:uint;
private var magazineCount:uint;
private var bulletsPerMagazine:uint;
private var magazineSwapDelay:uint;
private var magazineSwapCallback:Function;
private var magazineSwapSound:FlxSound;
private static const FIRE:uint = 0;
private static const FIRE_AT_MOUSE:uint = 1;
private static const FIRE_AT_POSITION:uint = 2;
private static const FIRE_AT_TARGET:uint = 3;
private static const FIRE_FROM_ANGLE:uint = 4;
private static const FIRE_FROM_PARENT_ANGLE:uint = 5;
/**
* Creates the FlxWeapon class which will fire your bullets.
* You should call one of the makeBullet functions to visually create the bullets.
* Then either use setDirection with fire() or one of the fireAt functions to launch them.
*
* @param name The name of your weapon (i.e. "lazer" or "shotgun"). For your internal reference really, but could be displayed in-game.
* @param parentRef If this weapon belongs to a parent sprite, specify it here (bullets will fire from the sprites x/y vars as defined below).
* @param xVariable The x axis variable of the parent to use when firing. Typically "x", but could be "screenX" or any public getter that exposes the x coordinate.
* @param yVariable The y axis variable of the parent to use when firing. Typically "y", but could be "screenY" or any public getter that exposes the y coordinate.
*/
public function FlxWeapon(name:String, parentRef:* = null, xVariable:String = "x", yVariable:String = "y")
{
this.name = name;
bounds = new FlxRect(0, 0, FlxG.width, FlxG.height);
positionOffset = new FlxPoint;
velocity = new FlxPoint;
if (parentRef)
{
setParent(parentRef, xVariable, yVariable);
}
}
/**
* Makes a pixel bullet sprite (rather than an image). You can set the width/height and color of the bullet.
*
* @param quantity How many bullets do you need to make? This value should be high enough to cover all bullets you need on-screen *at once* plus probably a few extra spare!
* @param width The width (in pixels) of the bullets
* @param height The height (in pixels) of the bullets
* @param color The color of the bullets. Must be given in 0xAARRGGBB format
* @param offsetX When the bullet is fired if you need to offset it on the x axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
* @param offsetY When the bullet is fired if you need to offset it on the y axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
*/
public function makePixelBullet(quantity:uint, width:int = 2, height:int = 2, color:uint = 0xffffffff, offsetX:int = 0, offsetY:int = 0):void
{
group = new FlxGroup(quantity);
for (var b:uint = 0; b < quantity; b++)
{
var tempBullet:Bullet = new Bullet(this, b);
tempBullet.makeGraphic(width, height, color);
group.add(tempBullet);
}
positionOffset.x = offsetX;
positionOffset.y = offsetY;
}
/**
* Makes a bullet sprite from the given image. It will use the width/height of the image.
*
* @param quantity How many bullets do you need to make? This value should be high enough to cover all bullets you need on-screen *at once* plus probably a few extra spare!
* @param image The image used to create the bullet from
* @param offsetX When the bullet is fired if you need to offset it on the x axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
* @param offsetY When the bullet is fired if you need to offset it on the y axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
* @param autoRotate When true the bullet sprite will rotate to match the angle of the parent sprite. Call fireFromParentAngle or fromFromAngle to fire it using an angle as the velocity.
* @param frame If the image has a single row of square animation frames on it, you can specify which of the frames you want to use here. Default is -1, or "use whole graphic"
* @param rotations The number of rotation frames the final sprite should have. For small sprites this can be quite a large number (360 even) without any problems.
* @param antiAliasing Whether to use high quality rotations when creating the graphic. Default is false.
* @param autoBuffer Whether to automatically increase the image size to accomodate rotated corners. Default is false. Will create frames that are 150% larger on each axis than the original frame or graphic.
*/
public function makeImageBullet(quantity:uint, image:Class, offsetX:int = 0, offsetY:int = 0, autoRotate:Boolean = false, rotations:uint = 16, frame:int = -1, antiAliasing:Boolean = false, autoBuffer:Boolean = false):void
{
group = new FlxGroup(quantity);
rotateToAngle = autoRotate;
for (var b:uint = 0; b < quantity; b++)
{
var tempBullet:Bullet = new Bullet(this, b);
if (autoRotate)
{
tempBullet.loadRotatedGraphic(image, rotations, frame, antiAliasing, autoBuffer);
}
else
{
tempBullet.loadGraphic(image);
}
group.add(tempBullet);
}
positionOffset.x = offsetX;
positionOffset.y = offsetY;
}
/**
* Makes an animated bullet from the image and frame data given.
*
* @param quantity How many bullets do you need to make? This value should be high enough to cover all bullets you need on-screen *at once* plus probably a few extra spare!
* @param imageSequence The image used to created the animated bullet from
* @param frameWidth The width of each frame in the animation
* @param frameHeight The height of each frame in the animation
* @param frames An array of numbers indicating what frames to play in what order (e.g. 1, 2, 3)
* @param frameRate The speed in frames per second that the animation should play at (e.g. 40 fps)
* @param looped Whether or not the animation is looped or just plays once
* @param offsetX When the bullet is fired if you need to offset it on the x axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
* @param offsetY When the bullet is fired if you need to offset it on the y axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
*/
public function makeAnimatedBullet(quantity:uint, imageSequence:Class, frameWidth:uint, frameHeight:uint, frames:Array, frameRate:uint, looped:Boolean, offsetX:int = 0, offsetY:int = 0):void
{
group = new FlxGroup(quantity);
for (var b:uint = 0; b < quantity; b++)
{
var tempBullet:Bullet = new Bullet(this, b);
tempBullet.loadGraphic(imageSequence, true, false, frameWidth, frameHeight);
tempBullet.addAnimation("fire", frames, frameRate, looped);
group.add(tempBullet);
}
positionOffset.x = offsetX;
positionOffset.y = offsetY;
}
/**
* Internal function that handles the actual firing of the bullets
*
* @param method
* @param x
* @param y
* @param target
* @return true if a bullet was fired or false if one wasn't available. The bullet last fired is stored in FlxWeapon.prevBullet
*/
private function runFire(method:uint, x:int = 0, y:int = 0, target:FlxSprite = null, angle:int = 0):Boolean
{
if (fireRate > 0 && (getTimer() < nextFire))
{
return false;
}
currentBullet = getFreeBullet();
if (currentBullet == null)
{
return false;
}
if (onPreFireCallback is Function)
{
onPreFireCallback.apply();
}
if (onPreFireSound)
{
onPreFireSound.play();
}
// Clear any velocity that may have been previously set from the pool
currentBullet.velocity.x = 0;
currentBullet.velocity.y = 0;
lastFired = getTimer();
nextFire = getTimer() + fireRate;
var launchX:int = positionOffset.x;
var launchY:int = positionOffset.y;
if (fireFromParent)
{
launchX += parent[parentXVariable];
launchY += parent[parentYVariable];
}
else if (fireFromPosition)
{
launchX += fireX;
launchY += fireY;
}
if (directionFromParent)
{
velocity = FlxVelocity.velocityFromFacing(parent, bulletSpeed);
}
// Faster (less CPU) to use this small if-else ladder than a switch statement
if (method == FIRE)
{
currentBullet.fire(launchX, launchY, velocity.x, velocity.y);
}
else if (method == FIRE_AT_MOUSE)
{
currentBullet.fireAtMouse(launchX, launchY, bulletSpeed);
}
else if (method == FIRE_AT_POSITION)
{
currentBullet.fireAtPosition(launchX, launchY, x, y, bulletSpeed);
}
else if (method == FIRE_AT_TARGET)
{
currentBullet.fireAtTarget(launchX, launchY, target, bulletSpeed);
}
else if (method == FIRE_FROM_ANGLE)
{
currentBullet.fireFromAngle(launchX, launchY, angle, bulletSpeed);
}
else if (method == FIRE_FROM_PARENT_ANGLE)
{
currentBullet.fireFromAngle(launchX, launchY, parent.angle, bulletSpeed);
}
if (onPostFireCallback is Function)
{
onPostFireCallback.apply();
}
if (onPostFireSound)
{
onPostFireSound.play();
}
bulletsFired++;
return true;
}
/**
* Fires a bullet (if one is available). The bullet will be given the velocity defined in setBulletDirection and fired at the rate set in setFireRate.
*
* @return true if a bullet was fired or false if one wasn't available. A reference to the bullet fired is stored in FlxWeapon.currentBullet.
*/
public function fire():Boolean
{
return runFire(FIRE);
}
/**
* Fires a bullet (if one is available) at the mouse coordinates, using the speed set in setBulletSpeed and the rate set in setFireRate.
*
* @return true if a bullet was fired or false if one wasn't available. A reference to the bullet fired is stored in FlxWeapon.currentBullet.
*/
public function fireAtMouse():Boolean
{
return runFire(FIRE_AT_MOUSE);
}
/**
* Fires a bullet (if one is available) at the given x/y coordinates, using the speed set in setBulletSpeed and the rate set in setFireRate.
*
* @param x The x coordinate (in game world pixels) to fire at
* @param y The y coordinate (in game world pixels) to fire at
* @return true if a bullet was fired or false if one wasn't available. A reference to the bullet fired is stored in FlxWeapon.currentBullet.
*/
public function fireAtPosition(x:int, y:int):Boolean
{
return runFire(FIRE_AT_POSITION, x, y);
}
/**
* Fires a bullet (if one is available) at the given targets x/y coordinates, using the speed set in setBulletSpeed and the rate set in setFireRate.
*
* @param target The FlxSprite you wish to fire the bullet at
* @return true if a bullet was fired or false if one wasn't available. A reference to the bullet fired is stored in FlxWeapon.currentBullet.
*/
public function fireAtTarget(target:FlxSprite):Boolean
{
return runFire(FIRE_AT_TARGET, 0, 0, target);
}
/**
* Fires a bullet (if one is available) based on the given angle
*
* @param angle The angle (in degrees) calculated in clockwise positive direction (down = 90 degrees positive, right = 0 degrees positive, up = 90 degrees negative)
* @return true if a bullet was fired or false if one wasn't available. A reference to the bullet fired is stored in FlxWeapon.currentBullet.
*/
public function fireFromAngle(angle:int):Boolean
{
return runFire(FIRE_FROM_ANGLE, 0, 0, null, angle);
}
/**
* Fires a bullet (if one is available) based on the angle of the Weapons parent
*
* @return true if a bullet was fired or false if one wasn't available. A reference to the bullet fired is stored in FlxWeapon.currentBullet.
*/
public function fireFromParentAngle():Boolean
{
return runFire(FIRE_FROM_PARENT_ANGLE);
}
/**
* Causes the Weapon to fire from the parents x/y value, as seen in Space Invaders and most shoot-em-ups.
*
* @param parentRef If this weapon belongs to a parent sprite, specify it here (bullets will fire from the sprites x/y vars as defined below).
* @param xVariable The x axis variable of the parent to use when firing. Typically "x", but could be "screenX" or any public getter that exposes the x coordinate.
* @param yVariable The y axis variable of the parent to use when firing. Typically "y", but could be "screenY" or any public getter that exposes the y coordinate.
* @param offsetX When the bullet is fired if you need to offset it on the x axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
* @param offsetY When the bullet is fired if you need to offset it on the y axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
* @param useDirection When fired the bullet direction is based on parent sprites facing value (up/down/left/right)
*/
public function setParent(parentRef:*, xVariable:String, yVariable:String, offsetX:int = 0, offsetY:int = 0, useDirection:Boolean = false):void
{
if (parentRef)
{
fireFromParent = true;
parent = parentRef;
parentXVariable = xVariable;
parentYVariable = yVariable;
positionOffset.x = offsetX;
positionOffset.y = offsetY;
directionFromParent = useDirection;
}
}
/**
* Causes the Weapon to fire from a fixed x/y position on the screen, like in the game Missile Command.
* If set this over-rides a call to setParent (which causes the Weapon to fire from the parents x/y position)
*
* @param x The x coordinate (in game world pixels) to fire from
* @param y The y coordinate (in game world pixels) to fire from
* @param offsetX When the bullet is fired if you need to offset it on the x axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
* @param offsetY When the bullet is fired if you need to offset it on the y axis, for example to line it up with the "nose" of a space ship, set the amount here (positive or negative)
*/
public function setFiringPosition(x:int, y:int, offsetX:int = 0, offsetY:int = 0):void
{
fireFromPosition = true;
fireX = x;
fireY = y;
positionOffset.x = offsetX;
positionOffset.y = offsetY;
}
/**
* The speed in pixels/sec (sq) that the bullet travels at when fired via fireAtMouse, fireAtPosition or fireAtTarget.
* You can update this value in real-time, should you need to speed-up or slow-down your bullets (i.e. collecting a power-up)
*
* @param speed The speed it will move, in pixels per second (sq)
*/
public function setBulletSpeed(speed:uint):void
{
bulletSpeed = speed;
}
/**
* The speed in pixels/sec (sq) that the bullet travels at when fired via fireAtMouse, fireAtPosition or fireAtTarget.
*
* @return The speed the bullet moves at, in pixels per second (sq)
*/
public function getBulletSpeed():uint
{
return bulletSpeed;
}
/**
* Sets the firing rate of the Weapon. By default there is no rate, as it can be controlled by FlxControl.setFireButton.
* However if you are firing using the mouse you may wish to set a firing rate.
*
* @param rate The delay in milliseconds (ms) between which each bullet is fired, set to zero to clear
*/
public function setFireRate(rate:uint):void
{
fireRate = rate;
}
/**
* When a bullet goes outside of this bounds it will be automatically killed, freeing it up for firing again.
* TODO - Needs testing with a scrolling map (when not using single screen display)
*
* @param bounds An FlxRect area. Inside this area the bullet should be considered alive, once outside it will be killed.
*/
public function setBulletBounds(bounds:FlxRect):void
{
this.bounds = bounds;
}
/**
* Set the direction the bullet will travel when fired.
* You can use one of the consts such as BULLET_UP, BULLET_DOWN or BULLET_NORTH_EAST to set the angle easily.
* Speed should be given in pixels/sec (sq) and is the speed at which the bullet travels when fired.
*
* @param angle The angle of the bullet. In clockwise positive direction: Right = 0, Down = 90, Left = 180, Up = -90. You can use one of the consts such as BULLET_UP, etc
* @param speed The speed it will move, in pixels per second (sq)
*/
public function setBulletDirection(angle:int, speed:uint):void
{
velocity = FlxVelocity.velocityFromAngle(angle, speed);
}
/**
* Sets gravity on all currently created bullets
* This will update ALL bullets, even those currently "in flight", so be careful about when you call this!
*
* @param xForce A positive value applies gravity dragging the bullet to the right. A negative value drags the bullet to the left. Zero disables horizontal gravity.
* @param yforce A positive value applies gravity dragging the bullet down. A negative value drags the bullet up. Zero disables vertical gravity.
*/
public function setBulletGravity(xForce:int, yForce:int):void
{
group.setAll("xGravity", xForce);
group.setAll("yGravity", yForce);
}
/**
* If you'd like your bullets to accelerate to their top speed rather than be launched already at it, then set the acceleration value here.
* If you've previously set the acceleration then setting it to zero will cancel the effect.
* This will update ALL bullets, even those currently "in flight", so be careful about when you call this!
*
* @param xAcceleration Acceleration speed in pixels per second to apply to the sprites horizontal movement, set to zero to cancel. Negative values move left, positive move right.
* @param yAcceleration Acceleration speed in pixels per second to apply to the sprites vertical movement, set to zero to cancel. Negative values move up, positive move down.
* @param xSpeedMax The maximum speed in pixels per second in which the sprite can move horizontally
* @param ySpeedMax The maximum speed in pixels per second in which the sprite can move vertically
*/
public function setBulletAcceleration(xAcceleration:int, yAcceleration:int, xSpeedMax:int, ySpeedMax:int):void
{
if (xAcceleration == 0 && yAcceleration == 0)
{
group.setAll("accelerates", false);
}
else
{
group.setAll("accelerates", true);
group.setAll("xAcceleration", xAcceleration);
group.setAll("yAcceleration", yAcceleration);
group.setAll("maxVelocityX", xSpeedMax);
group.setAll("maxVelocityY", ySpeedMax);
}
}
/**
* When the bullet is fired from a parent (or fixed position) it will do so from their x/y coordinate.
* Often you need to align a bullet with the sprite, i.e. to make it look like it came out of the "nose" of a space ship.
* Use this offset x/y value to achieve that effect.
*
* @param offsetX The x coordinate offset to add to the launch location (positive or negative)
* @param offsetY The y coordinate offset to add to the launch location (positive or negative)
*/
public function setBulletOffset(offsetX:int, offsetY:int):void
{
positionOffset.x = offsetX;
positionOffset.y = offsetY;
}
/**
* Give the bullet a random factor to its angle, speed, position or lifespan when fired. Can create a nice "scatter gun" effect.
*
* @param randomAngle The +- value applied to the angle when fired. For example 20 means the bullet can fire up to 20 degrees under or over its angle when fired.
* @param randomSpeed The +- value applied to the bullet speed when fired. For example 10 means the bullet speed varies by +- 10px/sec
* @param randomPosition The +- values applied to the x/y coordinates the bullet is fired from.
* @param randomLifeSpan The +- values applied to the life span of the bullet.
*/
public function setBulletRandomFactor(randomAngle:uint = 0, randomSpeed:uint = 0, randomPosition:FlxPoint = null, randomLifeSpan:uint = 0):void
{
rndFactorAngle = randomAngle;
rndFactorSpeed = randomSpeed;
if (randomPosition != null)
{
rndFactorPosition = randomPosition;
}
rndFactorLifeSpan = randomLifeSpan;
}
/**
* If the bullet should have a fixed life span use this function to set it.
* The bullet will be killed once it passes this lifespan (if still alive and in bounds)
*
* @param lifespan The lifespan of the bullet in ms, calculated when the bullet is fired. Set to zero to disable bullet lifespan.
*/
public function setBulletLifeSpan(lifespan:int):void
{
bulletLifeSpan = lifespan;
}
/**
* The elasticity of the fired bullet controls how much it rebounds off collision surfaces.
*
* @param elasticity The elasticity of the bullet between 0 and 1 (0 being no rebound, 1 being 100% force rebound). Set to zero to disable.
*/
public function setBulletElasticity(elasticity:Number):void
{
bulletElasticity = elasticity;
}
/**
* Internal function that returns the next available bullet from the pool (if any)
*
* @return A bullet
*/
private function getFreeBullet():Bullet
{
var result:Bullet = null;
if (group == null || group.length == 0)
{
throw new Error("Weapon.as cannot fire a bullet until one has been created via a call to makePixelBullet or makeImageBullet");
return null;
}
for each (var bullet:Bullet in group.members)
{
if (bullet.exists == false)
{
result = bullet;
break;
}
}
return result;
}
/**
* Sets a pre-fire callback function and sound. These are played immediately before the bullet is fired.
*
* @param callback The function to call
* @param sound An FlxSound to play
*/
public function setPreFireCallback(callback:Function = null, sound:FlxSound = null):void
{
onPreFireCallback = callback;
onPreFireSound = sound;
}
/**
* Sets a fire callback function and sound. These are played immediately as the bullet is fired.
*
* @param callback The function to call
* @param sound An FlxSound to play
*/
public function setFireCallback(callback:Function = null, sound:FlxSound = null):void
{
onFireCallback = callback;
onFireSound = sound;
}
/**
* Sets a post-fire callback function and sound. These are played immediately after the bullet is fired.
*
* @param callback The function to call
* @param sound An FlxSound to play
*/
public function setPostFireCallback(callback:Function = null, sound:FlxSound = null):void
{
onPostFireCallback = callback;
onPostFireSound = sound;
}
// TODO
public function TODOcreateBulletPattern(pattern:Array):void
{
// Launches this many bullets
}
public function update():void
{
// ???
}
}
}
================================================
FILE: src/org/flixel/plugin/photonstorm/PNGEncoder.as
================================================
/*
Copyright (c) 2008, Adobe Systems Incorporated
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Adobe Systems Incorporated nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.flixel.plugin.photonstorm
{
import flash.geom.*;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.utils.ByteArray;
/**
* Class that converts BitmapData into a valid PNG
*/
public class PNGEncoder
{
/**
* Created a PNG image from the specified BitmapData
*
* @param image The BitmapData that will be converted into the PNG format.
* @return a ByteArray representing the PNG encoded image data.
* @langversion ActionScript 3.0
* @playerversion Flash 9.0
* @tiptext
*/
public static function encode(img:BitmapData):ByteArray {
// Create output byte array
var png:ByteArray = new ByteArray();
// Write PNG signature
png.writeUnsignedInt(0x89504e47);
png.writeUnsignedInt(0x0D0A1A0A);
// Build IHDR chunk
var IHDR:ByteArray = new ByteArray();
IHDR.writeInt(img.width);
IHDR.writeInt(img.height);
IHDR.writeUnsignedInt(0x08060000); // 32bit RGBA
IHDR.writeByte(0);
writeChunk(png,0x49484452,IHDR);
// Build IDAT chunk
var IDAT:ByteArray= new ByteArray();
for(var i:int=0;i < img.height;i++) {
// no filter
IDAT.writeByte(0);
var p:uint;
var j:int;
if ( !img.transparent ) {
for(j=0;j < img.width;j++) {
p = img.getPixel(j,i);
IDAT.writeUnsignedInt(
uint(((p&0xFFFFFF) << 8)|0xFF));
}
} else {
for(j=0;j < img.width;j++) {
p = img.getPixel32(j,i);
IDAT.writeUnsignedInt(
uint(((p&0xFFFFFF) << 8)|
(p>>>24)));
}
}
}
IDAT.compress();
writeChunk(png,0x49444154,IDAT);
// Build IEND chunk
writeChunk(png,0x49454E44,null);
// return PNG
return png;
}
private static var crcTable:Array;
private static var crcTableComputed:Boolean = false;
private static function writeChunk(png:ByteArray,
type:uint, data:ByteArray):void {
if (!crcTableComputed) {
crcTableComputed = true;
crcTable = [];
var c:uint;
for (var n:uint = 0; n < 256; n++) {
c = n;
for (var k:uint = 0; k < 8; k++) {
if (c & 1) {
c = uint(uint(0xedb88320) ^
uint(c >>> 1));
} else {
c = uint(c >>> 1);
}
}
crcTable[n] = c;
}
}
var len:uint = 0;
if (data != null) {
len = data.length;
}
png.writeUnsignedInt(len);
var p:uint = png.position;
png.writeUnsignedInt(type);
if ( data != null ) {
png.writeBytes(data);
}
var e:uint = png.position;
png.position = p;
c = 0xffffffff;
for (var i:int = 0; i < (e-p); i++) {
c = uint(crcTable[
(c ^ png.readUnsignedByte()) &
uint(0xff)] ^ uint(c >>> 8));
}
c = uint(c^uint(0xffffffff));
png.position = e;
png.writeUnsignedInt(c);
}
}
}