In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: formation 9

Get Adobe Flash player
by Jenya 01 Jan 2014
/**
 * Copyright Jenya ( http://wonderfl.net/user/Jenya )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/bNha
 */

// forked from codeonwort's formation 9
// forked from codeonwort's formation 8
// forked from codeonwort's formation 7
// forked from codeonwort's formation 6
// forked from codefl's formation 5
// forked from codefl's formation 4
// forked from codefl's formation 3
package {
    
    import flash.display.DisplayObject
    import flash.display.Sprite
    import flash.display.MovieClip
    import flash.events.Event
    import flash.events.MouseEvent
    import flash.events.KeyboardEvent
    import flash.ui.Keyboard
    import flash.text.TextField
    import flash.geom.Rectangle
    
    import com.bit101.components.RadioButton
    
    /* 이번에 한 작업
    * 땅을 추가했다.
    * UI와 플레이 화면을 분리했다.
    * 플레이 화면을 스테이지보다 늘리고 스크롤이 되게 했다.
      근데 대각선을 고려 못했다 이건 나중에
    * 미니맵을 넣었다.
    * 유닛 그래픽을 변경했다. 깊이 정렬은 나중에
    * 하트 대형을 추가했다.
    */
    
    /* 앞으로 할 작업
    * Unit이 Shape를 상속하는 건 언젠가 고친다.
    * 유닛끼리 비켜가는 건 언제 구현하지 ㅏㅏㅏ 충돌검사 경로생성
    * IDestination 떡밥도 처리해야 하는디
    */
    
    // 메인 클래스(호스트 코드)
    public class FormationWorld extends Sprite {
        
        // ui box
        private var uiBox:Sprite
        private var minimap:Minimap
        private var numInput:TextField
        public static var formationView:TextField
        private var formationNames:Array = ["rect", "circle", "circular crowd",
                                            "wedge", "helix", "firework", "random", "heart"]
        private var formationIndex:int = 0
        
        // play screen
        private var playScreen:Sprite
        private var ground:Ground
        private var unitContainer:Sprite
        
        // services
        private var formCaller:FormationCaller
        private var squadSelector:SquadSelector
        private var moveMan:MoveManager
        private var fieldScroller:FieldScroller
        
        public function FormationWorld() {
            playScreen = addChild(new Sprite) as Sprite
            playScreen.y = 40         
            
            ground = new Ground(1000, 1000)
            ground.name = "ground"
            playScreen.addChild(ground)
            
            unitContainer = new Sprite
            unitContainer.mouseEnabled = false
            unitContainer.mouseChildren = false
            unitContainer.name = "unitContainer"
            playScreen.addChild(unitContainer)
            
            uiBox = new Sprite
            uiBox.graphics.beginFill(0)
            uiBox.graphics.drawRect(0, 0, stage.stageWidth, 40)
            addChild(uiBox)
            
            minimap = new Minimap(32, 32, playScreen)
            minimap.x = 200
            minimap.y = 4
            uiBox.addChild(minimap)
            
            makeUnits(91) // 인자는 유닛 개수
            setKeyboardAction()
            
            // 분대를 선택하는 역할을 한다
            squadSelector = new SquadSelector(playScreen, playScreen, unitContainer)
            
            // 언제 대형 함수를 호출할 지를 결정한다
            formCaller = new FormationCaller(this)
            formCaller.runClickMode(playScreen)
            
            moveMan = new MoveManager(unitContainer)
            moveMan.setMoveType(MoveManager.MANHATTAN)
            
            fieldScroller = new FieldScroller(playScreen, new Rectangle(0, 40, 465, 465-40))
            
            // radio buttons for click mode
            var clickMode1:RadioButton = new RadioButton(uiBox, stage.stageWidth-80, 5, "click", true, _setClickMode)
            var clickMode2:RadioButton = new RadioButton(uiBox, stage.stageWidth-80, 20, "timed click", false, _setTimedClickMode)
            clickMode1.groupName = clickMode2.groupName = "anotherGroupName"
            function _setClickMode():void { formCaller.runClickMode(playScreen) }
            function _setTimedClickMode():void { formCaller.runTimedClickMode(playScreen) }
            
            // radio buttons for move mode
            new RadioButton(uiBox, stage.stageWidth-160, 5, "manhattan", true, _setManhattanMove)
            new RadioButton(uiBox, stage.stageWidth-160, 20, "diagonal", false, _setDiagonalMove)
            function _setManhattanMove():void { moveMan.setMoveType(MoveManager.MANHATTAN) }
            function _setDiagonalMove():void { moveMan.setMoveType(MoveManager.DIAGONAL) }
        }
        
        public function makeFormation(centerX:Number, centerY:Number):void {
            var squad:Vector.<Unit> = squadSelector.selectedSquad
            var len:uint = squad.length
            var data:FormationData
                = FormationDataGenerator.gen(formationIndex, len, centerX, centerY)
            for(var i:int=0 ; i<len ; i++){
                squad[i].dest.x = data.dests[i].x
                squad[i].dest.y = data.dests[i].y
            }
        }
        
        private function makeUnits(num:uint):void {
            var unit:Unit, x0:Number, y0:Number
            for(var i:int=0 ; i<num ; i++){
                x0 = Math.random() * ground.width
                y0 = Math.random() * ground.height
                unit = new Unit(x0, y0)
                unitContainer.addChild(unit)
            }
        }
        
        private function setKeyboardAction():void {
            formationView = uiBox.addChild(new TextField) as TextField
            formationView.autoSize = "left"
            formationView.selectable = false
            formationView.textColor = 0xffffff
            formationView.text = "formation : " + formationNames[formationIndex]
            formationView.mouseEnabled = false
            stage.addEventListener("keyDown", changeFormation)
            function changeFormation(e:KeyboardEvent):void {
                switch(e.keyCode){
                    case Keyboard.LEFT :
                        formationIndex --
                        if(formationIndex == -1) formationIndex = FormationDataGenerator.length - 1
                        break
                    case Keyboard.RIGHT :
                        formationIndex ++
                        if(formationIndex == FormationDataGenerator.length) formationIndex = 0
                        break
                }
                formationView.text = "formation : " + formationNames[formationIndex]
            }
            
            var cap:TextField = uiBox.addChild(new TextField()) as TextField
            cap.selectable = false
            cap.autoSize = "left"
            cap.y = 20
            cap.textColor = 0xffffff
            cap.mouseEnabled = false
            cap.text = "write number of units and enter"
            
            numInput = uiBox.addChild(new TextField) as TextField
            numInput.text = String(unitContainer.numChildren)
            numInput.type = "input"
            numInput.autoSize = "left"
            numInput.background = true
            numInput.backgroundColor = 0xffff00
            numInput.textColor = 0x000000
            numInput.x = 160
            numInput.y = 20
            numInput.restrict = "0-9"
            numInput.maxChars = 4
            numInput.addEventListener("keyDown", changeNumUnits)
            function changeNumUnits(e:KeyboardEvent):void {
                if(e.keyCode == Keyboard.ENTER){
                    for(;unitContainer.numChildren;) unitContainer.removeChildAt(0)
                    makeUnits(int(numInput.text))
                }
            }
        } // setKeyboardAction() 끝
        
    }
    
}

//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////// 패키지 스코프의 바깥

//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// 서비스 코드
import flash.display.InteractiveObject
import flash.events.MouseEvent
import flash.events.Event
// 대형 함수를 호출한다
internal class FormationCaller {
    private var world:FormationWorld
    private var clk_io:InteractiveObject
    private var time_io:InteractiveObject
    public function FormationCaller(formationWorld:FormationWorld) {
        world = formationWorld
    }
    public function runClickMode(io:InteractiveObject):void {
        clearMode()
        io.addEventListener("mouseDown", clickMode_mouseDown)
        clk_io = io
    }
    private function clickMode_mouseDown(e:MouseEvent):void {
        if(e.target == clk_io && e.shiftKey){
            world.makeFormation(e.localX, e.localY)
        }
    }
    
    public function runTimedClickMode(io:InteractiveObject):void {
        clearMode()
        io.addEventListener("mouseDown", timedClickMode_mouseDown)
        time_io = io
    }
    private function timedClickMode_mouseDown(e:MouseEvent):void {
        if(e.target == time_io && e.shiftKey){
            time_io.addEventListener("enterFrame", timedClickMode_enterFrame)
            time_io.stage.addEventListener("mouseUp", timedClickMode_mouseUp)
            if(e.target == time_io) world.makeFormation(e.stageX, e.stageY)
        }
    }
    private function timedClickMode_enterFrame(e:Event):void {
        world.makeFormation(time_io.mouseX, time_io.mouseY)
    }
    private function timedClickMode_mouseUp(e:MouseEvent):void {
        time_io.removeEventListener("enterFrame", timedClickMode_enterFrame)
        time_io.stage.removeEventListener("mouseUp", timedClickMode_mouseUp)
    }
    
    private function clearMode():void {
        if(clk_io){
            clk_io.removeEventListener("mouseDown", clickMode_mouseDown)
            clk_io = null
        }
        if(time_io){
            time_io.removeEventListener("mouseDown", timedClickMode_mouseDown)
            time_io.removeEventListener("enterFrame", timedClickMode_enterFrame)
            time_io.stage.removeEventListener("mouseUp", timedClickMode_mouseUp)
            time_io = null
        }

    }

}

import flash.display.DisplayObjectContainer
import flash.display.InteractiveObject
import flash.display.Sprite
import flash.display.Shape
import flash.events.Event
import flash.events.MouseEvent
import flash.events.KeyboardEvent
import flash.events.EventDispatcher
internal class SquadSelector extends EventDispatcher {
    
    private var _io:InteractiveObject
    private var _doc:DisplayObjectContainer
    private var _unitContainer:Sprite
    private var _x0:Number, _y0:Number // start point
    private var _x1:Number, _y1:Number // end point
    private var _shape:Shape // area graphic
    
    private var _squad:Vector.<Unit>
    
    public function SquadSelector(io:InteractiveObject, doc:DisplayObjectContainer, unitContainer:Sprite) {
        _io = io
        _doc = doc
        _unitContainer = unitContainer
        _io.addEventListener("mouseDown", startDragArea, false, 1)
        _io.stage.addEventListener("keyDown", io_keyDown)
        _shape = new Shape
        _squad = new <Unit>[]
    }
    
    private function io_keyDown(e:KeyboardEvent):void {
        if((e.keyCode == 65 || e.keyCode == 97) && e.ctrlKey){
            _squad.length = 0
            var unit:Unit
            for(var i:int=0 ; i<_unitContainer.numChildren ; i++){
                unit = _unitContainer.getChildAt(i) as Unit
                unit.selected = true
                _squad.push(unit)
            }
        }
    }
    
    private function startDragArea(e:MouseEvent):void {
        if(e.target != _io) return
        if(e.shiftKey) return
        _io.addEventListener("enterFrame", dragArea)
        _io.addEventListener("mouseUp", stopDragArea)
        _x0 = e.localX
        _y0 = e.localY
        _doc.addChild(_shape)
    }
    private function dragArea(e:Event):void {
        _shape.graphics.clear()
        _shape.graphics.lineStyle(1, 0x00ff00)
        _shape.graphics.drawRect(_x0, _y0, _io.mouseX - _x0, _io.mouseY - _y0)
    }
    private function stopDragArea(e:MouseEvent):void {
        _shape.graphics.clear()
        _x1 = e.localX
        _y1 = e.localY
        _doc.removeChild(_shape)
        _io.removeEventListener("enterFrame", dragArea)
        _io.removeEventListener("mouseUp", stopDragArea)
        checkSelectedUnits()
    }
    private function checkSelectedUnits():void {
        _squad.length = 0
        var unit:Unit
        for(var i:int=0 ; i<_unitContainer.numChildren ; i++){
            unit = _unitContainer.getChildAt(i) as Unit
            if(step(_x0, unit.x, _x1) && step(_y0, unit.y, _y1)){
                unit.selected = true
                _squad.push(unit)
            }else unit.selected = false
        }
        function step(a:Number, b:Number, c:Number):Boolean {
            return (a <= b && b <= c) || (a >= b && b >= c)
        }
    }
    
    public function get selectedSquad():Vector.<Unit> { return _squad }
    
}

import flash.display.Sprite
import flash.events.Event
internal class MoveManager {
    
    public static const MANHATTAN:uint = 0
    public static const DIAGONAL:uint = 1
    
    private var types:Array = [manhattan, diagonal]
    private var _container:Sprite
    private var _currType:uint
    
    public function MoveManager(unitContainer:Sprite):void {
        _container = unitContainer
    }
    
    public function setMoveType(type:uint):void {
        _container.removeEventListener("enterFrame", types[_currType])
        _currType = type
        _container.addEventListener("enterFrame", types[type])
    }
    
    private function manhattan(e:Event):void {
        var speedX:Number = 5, speedY:Number = 5
        var unit:Unit
        for(var i:int=0 ; i<_container.numChildren ; i++){
            unit = _container.getChildAt(i) as Unit
            if(unit.x < unit.dest.x){
                if(unit.dest.x - unit.x > speedX) unit.vx = speedX
                else unit.vx = unit.dest.x - unit.x
            }
            else if(unit.x > unit.dest.x){
                if(unit.x - unit.dest.x > speedX) unit.vx = -speedX
                else unit.vx = unit.dest.x - unit.x
            }
            if(unit.y < unit.dest.y){
                if(unit.dest.y - unit.y > speedY) unit.vy = speedY
                else unit.vy = unit.dest.y - unit.y
            }
            else if(unit.y > unit.dest.y){
                if(unit.y - unit.dest.y > speedY) unit.vy = -speedY
                else unit.vy = unit.dest.y - unit.y
            }
            if(Math.abs(unit.x-unit.dest.x)<0.05) unit.vx = 0
            if(Math.abs(unit.y-unit.dest.y)<0.05) unit.vy = 0
            unit.x += unit.vx
            unit.y += unit.vy
        }
    }
    
    private function diagonal(e:Event):void {
        var speed:Number = 5
        var dx:Number, dy:Number, d:Number
        var unit:Unit
        for(var i:int=0 ; i<_container.numChildren ; i++){
            unit = _container.getChildAt(i) as Unit
            dx = unit.dest.x - unit.x
            dy = unit.dest.y - unit.y
            d = Math.sqrt(dx*dx + dy*dy)
            if(d <= speed){
                unit.x = unit.dest.x
                unit.y = unit.dest.y
                unit.vx = unit.vy = 0
            }else{
                dx /= d, dy /= d
                unit.vx = dx * speed
                unit.vy = dy * speed
            }
            unit.x += unit.vx
            unit.y += unit.vy
        }
    }
}

import flash.geom.ColorTransform
import flash.geom.Point
import flash.display.Bitmap
import flash.display.BitmapData
internal class Ground extends Bitmap {
    
    private static const MASK:uint = 0xEECBAD
    
    public function Ground(width:Number, height:Number) {
        super(new BitmapData(width, height, false, 0x0))
        
        // test
        bitmapData.perlinNoise(width/2, height/2, 5, Math.random()*int.MAX_VALUE, false, false, 7, true)
        cutout(bitmapData, bitmapData.rect, bitmapData, new Point, 5)
        var mask:BitmapData = new BitmapData(width, height, false, MASK)
        bitmapData.draw(mask, null, new ColorTransform(1,1,1,0.5))
        mask.dispose()
    }
    
    public static function cutout(src:BitmapData, srcRect:Rectangle, dst:BitmapData, dstPoint:Point,
                                                  levelOfDetails:uint=3):void {
        var R:Array = [], G:Array = [], B:Array = [], A:Array
        var i:int, a:Array = []
        
        for(i=0 ; i<=levelOfDetails ; i++) a[i] = uint(255 * i / levelOfDetails)
        a.push(a[i-1])
        
        if(src.transparent){
            A = []
            for(i=0 ; i<256 ; i++){
                if(i == a[0]) a.shift()
                B[i] = a[0]
                G[i] = B[i] << 8
                R[i] = G[i] << 8
                A[i] = R[i] << 8
            }
        }else{
            for(i=0 ; i<256 ; i++){
                if(i == a[0]) a.shift()
                B[i] = a[0]
                G[i] = B[i] << 8
                R[i] = G[i] << 8
            }
        }
        dst.paletteMap(src, srcRect, dstPoint, R, G, B, A)
    }
    
}

import flash.geom.Point
import flash.geom.Matrix
import flash.display.Shape
import flash.display.BitmapData
import flash.ui.Mouse
import flash.ui.MouseCursorData
internal class MouseSet {
    
    public static const LEFT_SCROLL:String = "left_scroll"
    public static const RIGHT_SCROLL:String = "right_scroll"
    public static const UP_SCROLL:String = "up_scroll"
    public static const DOWN_SCROLL:String = "down_scroll"
    
    static_init : {
        private static var sh:Shape = new Shape
        private static var mat:Matrix = new Matrix
        sh.graphics.beginFill(0xffffff, 1)
        sh.graphics.lineTo(16, 0)
        sh.graphics.lineTo(8, 16)
        sh.graphics.lineTo(0, 0)
        sh.graphics.endFill()
        
        private static var left_scroll:BitmapData = new BitmapData(16, 16, true, 0x0)
        mat.rotate(Math.PI/2)
        mat.tx = 16
        left_scroll.draw(sh, mat)
        
        private static var right_scroll:BitmapData = new BitmapData(16, 16, true, 0x0)
        mat.identity()
        mat.rotate(-Math.PI/2)
        mat.ty = 16
        right_scroll.draw(sh, mat)
        
        private static var up_scroll:BitmapData = new BitmapData(16, 16, true, 0x0)
        mat.identity()
        mat.rotate(Math.PI)
        mat.tx = mat.ty = 16
        up_scroll.draw(sh, mat)
        
        private static var down_scroll:BitmapData = new BitmapData(16, 16, true, 0x0)
        down_scroll.draw(sh)
        
        private static var left_scroll_data:MouseCursorData = new MouseCursorData
        left_scroll_data.data = new <BitmapData>[left_scroll]
        left_scroll_data.hotSpot = new Point(8, 8)
        Mouse.registerCursor(LEFT_SCROLL, left_scroll_data)
        
        private static var right_scroll_data:MouseCursorData = new MouseCursorData
        right_scroll_data.data = new <BitmapData>[right_scroll]
        right_scroll_data.hotSpot = new Point(8, 8)
        Mouse.registerCursor(RIGHT_SCROLL, right_scroll_data)
        
        private static var up_scroll_data:MouseCursorData = new MouseCursorData
        up_scroll_data.data = new <BitmapData>[up_scroll]
        up_scroll_data.hotSpot = new Point(8, 8)
        Mouse.registerCursor(UP_SCROLL, up_scroll_data)
        
        private static var down_scroll_data:MouseCursorData = new MouseCursorData
        down_scroll_data.data = new <BitmapData>[down_scroll]
        down_scroll_data.hotSpot = new Point(8, 8)
        Mouse.registerCursor(DOWN_SCROLL, down_scroll_data)
    }
    
    public static function setMouse(name:String):void {
        Mouse.cursor = name
    }
    
}

import flash.events.Event
import flash.events.MouseEvent
import flash.display.Sprite
import flash.geom.Rectangle
internal class FieldScroller {
    
    private var screen:Sprite
    private var screenW:Number, screenH:Number
    private var bound:Rectangle
    
    // 이러면 대각선 스크롤이 안 되잖아 또 고쳐야겠네
    private var link:Array = ['auto', MouseSet.LEFT_SCROLL,
                              MouseSet.RIGHT_SCROLL, MouseSet.UP_SCROLL,
                              MouseSet.DOWN_SCROLL]
    private var trans:Array = [new Point, new Point(10, 0),
                               new Point(-10, 0), new Point(0, 10),
                               new Point(0, -10)]
    private var state:int
    
    public function FieldScroller(playScreen:Sprite, bound:Rectangle) {
        screen = playScreen
        screenW = screen.getChildByName("ground").width
        screenH = screen.getChildByName("ground").height
        this.bound = bound
        screen.addEventListener("mouseMove", checkScroll)
        screen.addEventListener("mouseOut", releaseCursor)
        screen.addEventListener("enterFrame", scroll)
    }
    
    private function checkScroll(e:MouseEvent):void {
        state = mouseInEdge(e.stageX, e.stageY)
        MouseSet.setMouse(link[state])
    }
    private function mouseInEdge(mx:Number, my:Number):uint {
        var w:Number = 20, h:Number = 20
        if(mx <= bound.left + w) return 1
        else if(mx >= bound.right - w) return 2
        else if(my <= bound.top + h) return 3
        else if(my >= bound.bottom - h) return 4
        else return 0
    }
    
    private function releaseCursor(e:MouseEvent):void {
        MouseSet.setMouse("auto")
        state = 0
    }
    
    private function scroll(e:Event):void {
        screen.x += trans[state].x
        screen.y += trans[state].y
        if(screen.x > bound.left) screen.x = bound.left
        else if(screen.x + screenW < bound.right) screen.x = bound.right - screenW
        if(screen.y > bound.top) screen.y = bound.top
        else if(screen.y + screenH < bound.bottom) screen.y = bound.bottom - screenH
    }

}

import flash.geom.Point
import flash.events.Event
import flash.display.Sprite
import flash.display.Bitmap
import flash.display.BitmapData
import flash.display.DisplayObject
internal class Minimap extends Bitmap {
    
    private var zero:Point = new Point
    
    private var screen:Sprite
    private var unitCon:Sprite
    private var ground_bd:BitmapData
    
    private var rateX:Number, rateY:Number
    
    public function Minimap(WIDTH:Number, HEIGHT:Number, playScreen:Sprite) {
        screen = playScreen
        super(new BitmapData(WIDTH, HEIGHT, false, 0x0))
        addEventListener("enterFrame", update)
        
        var ground:Ground = screen.getChildByName("ground") as Ground
        rateX = width / ground.width
        rateY = height / ground.height
        var mat:Matrix = new Matrix(rateX, 0, 0, rateY)
        ground_bd = new BitmapData(WIDTH, HEIGHT, false, 0x0)
        ground_bd.draw(ground, mat)
        
        unitCon = screen.getChildByName("unitContainer") as Sprite
    }
    
    private function update(e:Event):void {
        bitmapData.copyPixels(ground_bd, ground_bd.rect, zero)
        
        var unit:DisplayObject
        var len:int = unitCon.numChildren
        for(var i:int = 0 ; i < len ; i++){
            unit = unitCon.getChildAt(i)
            bitmapData.setPixel(unit.x * rateX, unit.y * rateY, 0xff0000)
        }
    }
    
}

//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// 알고리즘 코드

import flash.display.Shape
internal class Unit extends Shape {
    public static const R:int = 5
    public var dest:IDestination
    public var vx:Number=0, vy:Number=0
    public function Unit(x0:Number, y0:Number){
        x = x0 ; y = y0
        dest = new Location(x0, y0)
        graphics.lineStyle(1, 0x0, 1)
        graphics.beginFill(0xff0000, 1)
        graphics.drawRect(-R/2, 0, R, -15)
        graphics.endFill()
    }
    public function set selected(v:Boolean):void {
        graphics.clear()
        if(v){
            graphics.lineStyle(1, 0x00ff00)
            graphics.drawEllipse(-R, -R/2, R*2, R*1.5)
        }
        graphics.lineStyle(1, 0x0, 1)
        graphics.beginFill(0xff0000)
        graphics.drawRect(-R/2, 0, R, -15)
    }
}

internal interface IDestination {
    function get x():Number
    function set x(val:Number):void
    function get y():Number
    function set y(val:Number):void
}
internal class Location implements IDestination {
    private var _x:Number, _y:Number
    public function Location(x0:Number, y0:Number) {
        _x = x0 ; _y = y0
    }
    public function get x():Number { return _x }
    public function set x(val:Number):void { _x = val }
    public function get y():Number { return _y }
    public function set y(val:Number):void { _y = val }
}
internal class UnitLocation implements IDestination {
    private var _u:Unit
    public function UnitLocation(u:Unit) {
        _u = u
    }
    public function get x():Number { return _u.x }
    public function get y():Number { return _u.y }
    public function set x(val:Number):void {}
    public function set y(val:Number):void {}
}

// 대형 구성 정보
import flash.geom.Point
internal class FormationData {
    public var dests:Vector.<Point>
    private var count:int = 0
    public function FormationData(len:uint) {
        dests = new Vector.<Point>(len, true)
        for(var i:int=0 ; i<dests.length ; i++) dests[i] = new Point
    }
    public function push(dx:Number, dy:Number):void {
        dests[count].x = dx
        dests[count].y = dy
        count ++
    }
    public function get full():Boolean { return count == dests.length }
    public function get extra():Boolean { return count != dests.length }
}

// 대형 발생기
import flash.geom.Point
internal class FormationDataGenerator {
    private static var list:Array = [f0, f1, f2, f3, f4, f5, f6, f7]
    private static var numUnits:uint
    private static var data:FormationData
    public static function get length():uint { return list.length }
    public static function gen(idx:int, num:uint, dx:Number, dy:Number):FormationData {
        numUnits = num
        data = new FormationData(num)
        list[idx](dx, dy)
        return data
    }

    // 직사각형
    public static function f0(destX:Number, destY:Number):void {
        var numColumns:int = Math.sqrt(numUnits)
        var numRows:int = numUnits / numColumns
        var numMods:int = numUnits - numColumns*numRows
        var spaceX:Number = Unit.R * 2, spaceY:Number = Unit.R * 2
        var LT_x:Number = destX - numRows*spaceX/2
        var LT_y:Number = destY - numColumns*spaceY/2
        
        var count:int = 0
        for(var column:int=0 ; column<numColumns ; column++){
            for(var row:int=0 ; row<numRows ; row++){
                data.push(LT_x + row*spaceX, LT_y + column*spaceY)
                count ++
            }
        }
        if(count < numUnits){
            var numRest:uint = numUnits - numRows*numColumns
            for(var i:int=0 ; i<numRest ; i++){
                data.push(LT_x + i*spaceX, LT_y + column*spaceY)
                count ++
            }
        }
    }
    
    // 원
    public static function f1(destX:Number, destY:Number):void {
        var R:Number = Math.min(300, numUnits*2)
        var t:Number
        for(var i:int=0 ; i<numUnits ; i++){
            t = 2*Math.PI*i/numUnits
            data.push(destX + R*Math.cos(t), destY + R*Math.sin(t))
        }
    }
    
    // 다층 원
    private static var circularCrowd_angle:Number = 0
    public static function f2(destX:Number, destY:Number):void {
        var rest:int = numUnits
        var need:int = Math.min(3, rest)
        var count:int = 0
        var R:Number = Unit.R * 2
        var t:Number = circularCrowd_angle += 1 * Math.PI/180
        
        var k:Number
        while(true){
            for(var i:int=0 ; i<need ; i++){
                k = 2*Math.PI * i/need
                data.push(destX + R*Math.cos(k+t), destY + R*Math.sin(k+t))
                count ++
            }
            rest -= need
            if(rest <= 0) break
            need = Math.min(rest, need+5)
            R += Unit.R * 3
        }
    }
    
    // 쐐기
    public static function f3(cx:Number, cy:Number):void {
        var rest:uint = numUnits
        var num:uint = 1, num_inc:uint = 8
        var rowSpace:Number = Unit.R * 6
        var spaceX:Number = Unit.R * 2.5
        var spaceY:Number = Unit.R * 2
        
        var i:int, half:uint, other:uint
        while(rest > 0){
            if(rest <= num){ num=rest ; rest=0 }
            else{ rest -= num }
            
            cx -= rowSpace
            half = num / 2
            other = num - half
            if(num & 1) { data.push(cx, cy) ; other-- } 
            for(i = 1 ; i <= half ; i++) data.push(cx + spaceX*i, cy - spaceY*i)
            for(i = 1 ; i <= other ; i++) data.push(cx + spaceX*i, cy + spaceY*i)
            num += num_inc
        }
    }
    
    // 나선
    public static function f4(cx:Number, cy:Number):void {
        var t:Number = 0, r:Number = Unit.R * 2
        var rInc:Number = Unit.R / 4
        
        for(var i:int = 0 ; i < numUnits ; i++){
            data.push( cx + r*Math.cos(t), cy + r*Math.sin(t) )
            t += Math.acos((r*r + (r+rInc)*(r+rInc) - Unit.R*Unit.R*4) / (2*r*(r+rInc)))
            r += rInc
        }
    }
    
    // 불꽃놀이
    private static var fire_angle:Number = 30 * Math.PI/180
    public static function f5(cx:Number, cy:Number):void {
        var rest:uint = numUnits
        var t:Number = 0, r:Number = Unit.R * 2, space:Number = Unit.R * 2
        var numNiddles:uint = 8, tInc:Number = 2*Math.PI / numNiddles
        var limit:uint = numNiddles
        var angle:Number = fire_angle += Math.PI/180
        
        var c:int = 0
        for(var i:int = 0 ; i <= limit ; i++){
            if(i == numNiddles){
                rest -= numNiddles
                if(rest <= 0) break
                else{
                    i = 0
                    r += space
                    t = 0
                    limit = Math.min(rest, numNiddles)
                }
            }
            data.push(cx + r * Math.cos(angle + t), cy + r * Math.sin(angle + t))
            t += tInc
            c++
            if(c == numUnits) break
        }
    }
    
    // 랜덤 (더블클릭으로 하는 거였는데 시리즈에 추가함)
    public static function f6(cx:Number, cy:Number):void {
        for(var i:int=0 ; i<numUnits ; i++){
            data.push(Math.random()*500, Math.random()*500)
        }
    }
    
    // 하트
    public static function f7(cx:Number, cy:Number):void {
        var n:uint = 2
        while(true){
            if(numUnits <= (19*n - 12) * (n-1) + 1) break
            n++
        }
        
        var spaceX:Number = Unit.R * 2
        var spaceY:Number = Unit.R * 2.5
        var half:uint = n * (19*n - 35) * .5 + 8
        var center:uint = half + 4*n - 3
        var c:uint = 1 // count
        var col:uint = 1, col2:uint = 1
        var col_cnt:uint = 0 // column count
        var col_len:uint = n
        
        var pos:Point
        var axisX:Number = 2 * (cx + spaceX * (3*n-2))
        var c2:uint = half - 1
        
        while(c <= numUnits){
            if(c <= half){
                if(col <= n){
                    data.push(cx + col * spaceX, cy + (n-col + col_cnt) * spaceY)
                    col_cnt ++
                    if(col_cnt == col_len){
                        col_cnt = 0
                        col_len += 2
                        col ++
                        if(col == n+1) col_len --
                    }
                }else if(col <= 2*n - 2){
                    data.push(cx + col * spaceX, cy + col_cnt * spaceY)
                    col_cnt ++
                    if(col_cnt == col_len){
                        col_cnt = 0
                        col_len += 1
                        col ++
                    }
                }else{
                    data.push(cx + col * spaceX, cy + (col-(2*n-1)+col_cnt) * spaceY)
                    col_cnt ++
                    if(col_cnt == col_len){
                        col_cnt = 0
                        col ++
                    }
                }
            }else if(c <= center){
                data.push(cx + col * spaceX, cy + (n-1+col_cnt) * spaceY)
                col_cnt ++
                if(col_cnt == col_len){
                    col_cnt = 0
                }
            }else{
                pos = data.dests[c2--]
                data.push(axisX - pos.x, pos.y)
            }
            c++
        }
    }

}