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

Level

this code is licensed under a Creative Commons Attribution-Share Alike license
see http://creativecommons.org/licenses/by-sa/3.0/ for details
Get Adobe Flash player
by Player_03 04 Jun 2009
    Embed
//this code is licensed under a Creative Commons Attribution-Share Alike license
//see http://creativecommons.org/licenses/by-sa/3.0/ for details

package {
    import flash.display.Sprite;
    import flash.utils.Dictionary;
    import flash.utils.ByteArray;
    import flash.utils.getTimer;
    import flash.text.*;
    import flash.events.*;
    import flash.geom.Point;
    
    public class Level extends Sprite {
        //all variables are defined above the methods in which they are used
        
        //sets up the level and stage
        public function Level() {
            stage.showDefaultContextMenu = false;
            //stage.frameRate = 10;
            createDictionary();
            createLevelHolder();
            createInput();
            createOutput();
            setupFocus();
            registerEvents();
            createLevel("5|5|i000I0000000p0000000T000t");
                      //"7|7|p000000000000000trT0000bgb0000Iri0000000000000000");
        }
        
        ////////========Physics========////////
        
        private var paused:Boolean = true;
        private var initialX:Number, initialY:Number;
        //resets the player and all physics-related variables
        private function resetLevel():void {
            yVel = 0;
            xVel = 0;
            if(player != null) {
                player.x = initialX;
    	        player.y = initialY;
            }
        }
        
        private var xVel:Number = 0, yVel:Number = 0;
        //runs the physics for the given time in milliseconds
        private function runPhysics(time:int):void {
            if(player == null || paused) return;
            
            //set the velocity based on user input
            //yVel += .0001*blockSize*time;
            if(isDown(37)) {
                xVel -= .0001*blockSize*time;
            }
            if(isDown(39)) {
                xVel += .0001*blockSize*time;
            }
            if(isDown(40)) {
                yVel += .0001*blockSize*time;
            }
            if(isDown(38)) {
                yVel -= .0001*blockSize*time;
            }
            xVel *= 0.99;
            yVel *= 0.99;
            
            
            //find the farthest the player can move
            var newPos:Point = new Point(player.x + xVel, player.y + yVel),
                collisionData:CollisionData = new CollisionData(newPos, null);
            
            //while(newPos.x != player.x || newPos.y != player.y) {
                for(var r:int = 0; r < level.length; r++) {
                    for(var c:int = 0; c < level[0].length; c++) {
                        collisionData = collide(r, c, collisionData);
                    }
                }
                collisionData = collide(-1, -1, collisionData);
                
                
                
                //if the player hit anything, update its velocity
                if(collisionData.moveTo) {
                    /*if(collisionData.objectHit is LineSegment) {
                        var lineHit:LineSegment = (LineSegment)(collisionData.objectHit),
                            slope1:Number = lineHit.slope, slope2:Number = -1/slope1,
                            onePoint:Point = lineHit.start;
                        
                        
                        if(slope1 == Infinity) {
                            //TODO
                        } else if(slope1 == 0) {
                            //TODO
                        } else {
                            //use a formula for finding the projection of one vector onto another
                            xVel = (slope1*onePoint.x + slope2*newPos.x + newPos.y - onePoint.y)
                                   / (slope1 + slope2);
                            yVel = slope1*(xVel - onePoint.x);
                            xVel -= onePoint.x;
                        }
                    }*/
                    
                    newPos = collisionData.moveTo;
                    xVel = newPos.x - player.x;
                    yVel = newPos.y - player.y;
                    player.x = newPos.x;
                    player.y = newPos.y;
                    /*newPos.x += xVel;
                    newPos.y += yVel;*/
                } else {
                    player.x = newPos.x;
                    player.y = newPos.y;
                    //break;
                }
            //}
        }
        
        //checks whether the player would touch the item at position (r, c)
        //if moved to newPos and returns the farthest point that
        //the player can move to without collision
        private function collide(r:int, c:int,
                            lastCollision:CollisionData):CollisionData {
            var newPos:Point = lastCollision.hitLocation, farthest:Point = newPos;
            
            //if the player can't move, no more testing is necessary
            if(player.x == newPos.x && player.y == newPos.y)
                return(lastCollision);
            
            //get the object data and stop if there is no object
            var ob:Block = (r >= 0 ? level[r][c] : background),
                points:Array = null, halfSize:Number = blockSize * 0.5;
            if(ob == null || ob is Player)
                return(lastCollision);
            
            points = ob.points;
            
            //declare variables to be used in the loop here
            var deltaX:Number, deltaY:Number,
                  prevPoint:Point = points[points.length-1],
                  lineStart:Point, lineEnd:Point, collision:Point,
                  objectHit:Object = lastCollision.objectHit,
                  moveTo:Point = lastCollision.moveTo;
            
            //update prevPoint to reflect its position on the stage
            prevPoint = new Point(prevPoint.x + ob.x, prevPoint.y + ob.y);
            
            
            //iterate through all the points and check for collisions
            //both with the points and the lines between them
            for each(var p:Point in points) {
                //update p to reflect its position on the stage
                p = new Point(p.x + ob.x, p.y + ob.y);
                
                //hittest the point
                //TODO: add a point hittest
                
                //hittest the line
                
                //get the horizontal/vertical distance travelled
                deltaX = newPos.x - player.x;
                deltaY = newPos.y - player.y;
                
                //find the path of the point on the circle
                //that would hit the current line first
                lineStart = radialPoint(p, prevPoint);
                lineEnd = new Point(lineStart.x + deltaX, lineStart.y + deltaY);
                
                //check to see if the point actually hit
                collision = segmentIntersection(lineStart, lineEnd, prevPoint, p);
                if(collision != null) {
                    collision.x += player.x - lineStart.x;
                    collision.y += player.y - lineStart.y;
                    
                    if(Math.abs(farthest.x - player.x) > Math.abs(collision.x - player.x)
                       || Math.abs(farthest.y - player.y) > Math.abs(collision.y - player.y)) {
                       farthest = collision;
                       objectHit = new LineSegment(lineStart, lineEnd);
                       moveTo = new Point(farthest.x - (lineStart.x - player.x) * 0.002,
                                          farthest.y - (lineStart.y - player.y) * 0.002);
                    }
                } else {
                    //TODO: check if the player is overlapping the triangle
                    //and output data if so
                }
                
                prevPoint = p;
            }
            
            return(new CollisionData(farthest, objectHit, moveTo));
        }
        
        //returns the point on the circle at the end of the radius perpendicular to the given line segment
        private function radialPoint(p1:Point, p2:Point):Point {
            var radius:Number = blockSize/2;
            
            //check to see if the line is horizontal or vertical
            //and, if so, return the appropriate point
            if(p1.x == p2.x) {
                return(new Point(player.x
                             + ((player.x < p1.x) ? radius : -radius), player.y));
            }
            if(p1.y == p2.y) {
                return(new Point(player.x, player.y
                                        + ((player.y < p1.y) ? radius : -radius)));
            }
            
            //find the slope of the radius perpendicular to the line
            var rSlope:Number = -(p2.x - p1.x) / (p2.y - p1.y),
            
            //find and return the point at the end
                rPoint:Point = new Point(0, 0);
            rPoint.x = player.x + Math.sqrt(radius*radius / (rSlope*rSlope + 1))
                       * ((rSlope < 0) != ((p1.x-player.x) / rSlope + p1.y < player.y) ? -1 : 1);
            rPoint.y = rSlope * (rPoint.x - player.x) + player.y;
            return(rPoint);
        }
        
        //draws a box around the given point
        private function displayPoint(p:Point):void {
            with(visualOutput.graphics) {
                lineStyle(1);
                moveTo(p.x + 1, p.y + 1);
                lineTo(p.x - 1, p.y + 1);
                lineTo(p.x - 1, p.y - 1);
                lineTo(p.x + 1, p.y - 1);
                lineTo(p.x + 1, p.y + 1);
            }
        }
        
        //draws the given line
        private function displayLine(p1:Point, p2:Point):void {
            with(visualOutput.graphics) {
                lineStyle(1, 0xAAAAAA);
                moveTo(p1.x, p1.y);
                lineTo(p2.x, p2.y);
            }
        }
        
        //gets the point of intersection the line segments
        //defined by p11->p12 and p21->p22
        //or returns null if they don't intersect
        private function segmentIntersection(p11:Point, p12:Point,
                                             p21:Point, p22:Point):Point {
            //get the slopes of the two lines
            var slope1:Number = (p12.y - p11.y) / (p12.x - p11.x),
                slope2:Number = (p22.y - p21.y) / (p22.x - p21.x);
            //stop if the lines are parallel
            if(slope1 == slope2 || p11.x == p12.x && p21.x == p22.x)
                return(null);
            
            var intersection:Point;
            
            //check if either line is vertical or near vertical
            if(Math.abs(slope1) > 100) {
                intersection = new Point(p11.x, slope2 * (p11.x - p21.x) + p21.y);
            } else if(Math.abs(slope2) > 100) {
                intersection = new Point(p21.x, slope1 * (p21.x - p11.x) + p11.y);
            //check if either line is horizontal
            } else {
                //use a formula for the intersection of non-vertical lines
                
                //find the x coordinate of the intersection
                intersection = new Point((slope1*p11.x - slope2*p21.x + p21.y - p11.y)
                                         / (slope1 - slope2), 0);
                //use the x coordinate to find y
                intersection.y = slope1 * (intersection.x - p11.x) + p11.y;
            }
            
            //return the intersection between the lines if it is also on both segments
            if(isBetween(intersection.x, p11.x, p12.x)
               && isBetween(intersection.x, p21.x, p22.x)
               && isBetween(intersection.y, p11.y, p12.y)
               && isBetween(intersection.y, p21.y, p22.y))
                return(intersection);
            
            return(null);
        }
        
        //checks if the first value is between the other two
        private function isBetween(v1:Number, v2:Number, v3:Number):Boolean {
            //add or subtract 0.000001 to compensate for rounding errors
            if(v2 < v3)
                return(v2 - 0.000001 < v1 && v1 < v3 + 0.000001);
            return(v3 - 0.000001 < v1 && v1 < v2 + 0.000001);
        }
        
        
        ////////========Level creation========////////
        
        private var level:Array;
        private var parser:RegExp = /(\d+)\|(\d+)\|([a-zA-Z0-9]+)/;
        private var blockSize:int = 50;
        private var player:Player, levelHolder:Sprite, background:Block;
        
        //parses the given string and builds a level from it
        private function createLevel(data:String):void {
            //parse the data
            var match:Object = parser.exec(data);
            if(match == null || match[0] == input.text)
                return;
            
            
            //unload the previous level, if there was one
            if(level != null) {
                for each(var a:Array in level) 
                    for each(var cur:Block in a)
                        try {
                            levelHolder.removeChild(cur);
                        } catch(e:Error) {}
                try {
                    levelHolder.removeChild(player);
                } catch(e:Error) {}
                player = null;
            }
            
            
            //iterate through the data, placing blocks at the appropriate places
            var curBlock:Block, curLetter:String,
                height:int = parseInt(match[1]), width:int = parseInt(match[2]);
            level = new Array(height);
            data = match[3];
            for(var r:int = 0; r < height; r++) {
                level[r] = new Array(width);
                for(var c:int = 0; c < width; c++) {
                    curLetter = data.charAt(r*width + c);
                    if(curLetter in f) {
                        curBlock = f[curLetter]();
                    } else {
                        curBlock = null;
                    }
                    if(curBlock != null) {
                        curBlock.x = (c + 0.5)*blockSize;
                        curBlock.y = (r + 0.5)*blockSize;
                        levelHolder.addChild(curBlock);
                    }
                    level[r][c] = curBlock;
                }
            }
            
            if(player != null) {
                //record the player's initial position
                initialX = player.x;
                initialY = player.y;
            }
            
            
            //update the background to match the current level size
            background.points[2].x = background.points[3].x = blockSize * width;
            background.points[1].y = background.points[2].y = blockSize * height;
            background.redraw();
            
            //output the current data, adding zeros to the end if the string isn't long enough
            var extraZeros:String = "", blankSpaces:int = width*height - data.length;
            if(blankSpaces < 0) {
                input.text = match[0].substring(0, match[0].length + blankSpaces);
            } else {
                for(; blankSpaces > 0; blankSpaces--) {
                    extraZeros += "0";
                }
                input.text = match[0] + extraZeros;
            }
        }
        
        //return null to indicate a blank space
        private function blank():Block {
            return(null);
        }
        
        //create squares of the given colors
        private function graySquare():Square {
            return(new Square(blockSize));
        }
        private function redSquare():Square {
             return(new Square(blockSize, 0xFF0000));
        }
        private function greenSquare():Square {
             return(new Square(blockSize, 0x00FF00));
        }
        private function blueSquare():Square {
             return(new Square(blockSize, 0x0000FF));
        }
        
        //create triangles facing in various directions
        private function triangle0():Triangle {
            return(new Triangle(blockSize, 0));
        }
        private function triangle1():Triangle {
            return(new Triangle(blockSize, 1));
        }
        private function triangle2():Triangle {
            return(new Triangle(blockSize, 2));
        }
        private function triangle3():Triangle {
            return(new Triangle(blockSize, 3));
        }
        
        
        //creates a circle to serve as the player
        private function makePlayer(color:int = 0x5555FF, lineColor:int = 0x000000):Player {
            if(player == null)
                player = new Player(blockSize, color, lineColor);
            return(player);
        }
        
        private var f:Dictionary = new Dictionary();
        //creates the dictionary mapping letters to block types
        private function createDictionary():void {
            f["0"] = blank;
            f["d"] = graySquare;
            f["r"] = redSquare;
            f["g"] = greenSquare;
            f["b"] = blueSquare;
            f["p"] = makePlayer;
            f["t"] = triangle0;
            f["T"] = triangle1;
            f["i"] = triangle2;
            f["I"] = triangle3;
        }
        
        
        ////////========Interaction========////////
        
        //sets up the events for movement
        private function registerEvents():void {
            addEventListener("enterFrame", onEnterFrame);
            keysPressed.position = 100;
            keysPressed.writeBoolean(false);
            addEventListener("keyDown", onKeyDown);
            addEventListener("keyUp", onKeyUp);
            levelHolder.addEventListener("mouseDown", onMouseDown);
            levelHolder.addEventListener("mouseMove", onMouseMove);
        }
        
        private var focusHolder:Sprite;
        //sets up the focus handling
        private function setupFocus():void {
            focusHolder = new Sprite();
            addChild(focusHolder);
        }
        
        private var lastFrame:int = 0;
        //makes regular focus checks
        //applies the physics
        private function onEnterFrame(e:Event):void {
            if(stage.focus == null)
                stage.focus = focusHolder;
            if(isDown(13) && stage.focus == input)
                onSubmit(new Event(""));
            
            runPhysics(getTimer() - lastFrame);
            lastFrame = getTimer();
        }
        
        private var keysPressed:ByteArray = new ByteArray();
        //handles key down events
        private function onKeyDown(e:KeyboardEvent):void {
            keysPressed.position = e.keyCode;
            keysPressed.writeBoolean(true);
        }
        
        //handles key up events
        private function onKeyUp(e:KeyboardEvent):void {
            keysPressed.position = e.keyCode;
            keysPressed.writeBoolean(false);
        }
        
        //checks if the given key is down
        private function isDown(code:uint):Boolean {
            keysPressed.position = code;
            return(keysPressed.readBoolean());
        }
        
        private var s:Point = null, end:Point = null;
        //updates the value of add based on whether the mouse is over a tile
        private function onMouseDown(e:MouseEvent):void {
            onMouseMove(e, true);
        }
        
        private var add:Boolean = true, curType:String = "d";
        //adds or removes the block at the mouse's location
        private function onMouseMove(e:MouseEvent, justPressed:Boolean = false):void {
            if(!e.buttonDown) {
                paused = false;
                return;
            }
            
            resetLevel();
            paused = true;
            
            var c:int = int(levelHolder.mouseX / blockSize);
            var r:int = int(levelHolder.mouseY / blockSize);
            
            if(r < 0 || r >= level.length || c < 0 || c >= level[0].length) {
                return;
            }
            
            if(justPressed)
                add = level[r][c] == null;
            
            //define variables here to avoid duplicate variable definitions
            var secondBar:int, charPos:int, newString:String;
            if(level[r][c] != null) {
                if(!add) {
                    //remove the block
                    try {
                        levelHolder.removeChild(level[r][c]);
                    } catch(e:Error) {}
                    if(level[r][c] == player)
                        player = null;
                    level[r][c] = null;
                    
                    //update the input field string
                    secondBar = input.text.indexOf("|", input.text.indexOf("|") + 1);
                    charPos = secondBar + r*level[0].length + c + 1;
                    newString = input.text;
                    newString = newString.substring(0, charPos) + "0" + newString.substring(charPos + 1);
                    input.text = newString;
                }
            } else {
                if(add) {
                    //add a new block
                    var curBlock:Block = f[curType]();
                    curBlock.x = (c + 0.5) * blockSize;
                    curBlock.y = (r + 0.5) * blockSize;
                    levelHolder.addChild(curBlock);
                    level[r][c] = curBlock;
                    
                    //update the input field string
                    secondBar = input.text.indexOf("|", input.text.indexOf("|") + 1);
                    charPos = secondBar + r*level[0].length + c + 1;
                    newString = input.text;
                    newString = newString.substring(0, charPos) + "d" + newString.substring(charPos + 1);
                    input.text = newString;
                }
            }
        }
        
        
        ////////========Input and output========////////
        
        private var input:TextField, output:TextField, visualOutput:Sprite;
        //sets up the input text field
        private function createInput():void {
            if(input != null) return;
            input = new TextField();
            input.height = 50;
            input.width = 400;
            input.x = 33;
            input.y = 350;
            input.border = true;
            input.addEventListener("focusOut", onSubmit);
            input.type = "input";
            input.wordWrap = true;
            input.restrict = "0-9\\|a-zA-Z";
            addChild(input);
        }
        
        //sets up the output text field that is used for the trace() function
        private function createOutput():void {
            if(output != null) return;
            output = new TextField();
            output.text = "Output:\n";
            output.height = 64;
            output.width = 400;
            output.x = 33;
            output.y = 400;
            output.multiline = true;
            output.mouseWheelEnabled = true;
            addChild(output);
            
            visualOutput = new Sprite();
            addChild(visualOutput);
        }
        
        //creates the level holder and a white square to define its boundaries
        private function createLevelHolder():void {
            levelHolder = new Sprite();
            addChild(levelHolder);
            
            background = new Block(new Array(new Point(0, 0),
                                             new Point(0, 1),
                                             new Point(1, 1),
                                             new Point(1, 0)));
            levelHolder.addChild(background);
            background.alpha = 0;
        }
        
        //reloads the level when the user clicks out of the text field
        private function onSubmit(e:Event):void {
            createLevel(input.text);
        }
        
        //outputs the given data
        private function trace(... r):void {
            //convert all given objects to strings and concatenate them
            var string:String = "";
            for each(var o:Object in r) {
                if(o == null)
                    string += "null, ";
                else
                    string += o.toString() + ", ";
            }
            string = string.substring(0, string.length-2);
            
            //add line numbers to the beginning of all new lines in the string
            var numLines:int = output.numLines - 1;
            var toPrint:String = "";
            var lines:Array = string.split("\n");
            for each(var s:String in lines) {
                toPrint +=  numLines.toString() + ":  " + s + "\n";
                numLines++;
            }
            
            //append the text and scroll a very large number of lines down (it stops at the end)
            output.appendText(toPrint);
            output.scrollV = numLines;
        }
    }
}

import flash.display.Sprite;
import flash.geom.Point;

class Block extends Sprite {
    public var points:Array;
    private var color:int, lineColor:int;
    
    public function Block(inputPoints:Array = null,
                    color:int = 0x888888, lineColor:int = 0x000000) {
        if(inputPoints == null || inputPoints.length < 2) {
            //leave a blank space
            points = null;
        } else {
            points = inputPoints;
            
            this.color = color;
            this.lineColor = lineColor;
            redraw();
        }
    }
    
    public function redraw():void {
        graphics.clear();
        graphics.lineStyle(1, lineColor);
        graphics.beginFill(color);
        
        graphics.moveTo(points[0].x, points[0].y);
        for(var i:int = 1; i < points.length; i++) {
            graphics.lineTo(points[i].x, points[i].y);
        }
        graphics.lineTo(points[0].x, points[0].y);
        graphics.endFill();
    }
}

class Square extends Block {
    public function Square(size:int = 1,
                    color:int = 0x888888, lineColor:int = 0x000000) {
        var halfSize:Number = size / 2;
        points = new Array();
        points[0] = new Point(-halfSize, -halfSize);
        points[1] = new Point(halfSize, -halfSize);
        points[2] = new Point(halfSize, halfSize);
        points[3] = new Point(-halfSize, halfSize);
        super(points, color, lineColor);
    }
}

class Triangle extends Block {
    public function Triangle(size:int = 1, direction:int = 0,
                    color:int = 0x888888, lineColor:int = 0x000000) {
        var halfSize:Number = size / 2;
        points = new Array();
        points[0] = new Point(-halfSize, -halfSize);
        points[1] = new Point(halfSize, -halfSize);
        points[2] = new Point(halfSize, halfSize);
        points[3] = new Point(-halfSize, halfSize);
        
        try {
            points.splice(direction, 1);
        } catch(e:Error) {
            points.splice(0, 1);
        }
        
        super(points, color, lineColor);
    }
}

class Player extends Block {
    public function Player(size:int = 1,
                    color:int = 0x888888, lineColor:int = 0x000000) {
        graphics.lineStyle(1, lineColor);
        graphics.beginFill(color);
        graphics.drawCircle(0, 0, size/2);
        graphics.endFill();
        
        //don't have the Block class do anything
        super();
    }
}


class CollisionData {
    public var hitLocation:Point, objectHit:Object, moveTo:Point;
    
    public function CollisionData(hitLoc:Point, obHit:Object = null, mTo:Point = null) {
        hitLocation = hitLoc;
        objectHit = obHit;
        moveTo = mTo;
    }
}


class LineSegment {
    private var p1:Point, p2:Point, m:Number;
    
    public function LineSegment(s:Point, e:Point) {
        p1 = s;
        p2 = e;
        
        if(p1.x == p2.x)
            m = Infinity;
        else
            m = (p2.y - p1.y) / (p2.x - p1.x);
    }
    
    public function get start():Point {
        return(p1);
    }
    
    public function get end():Point {
        return(p2);
    }
    
    public function get slope():Number {
        return(m);
    }
}