package { import flash.display.Shape; import flash.display.Sprite; import flash.display.StageScaleMode; import flash.display.StageAlign; import flash.text.TextField; import flash.text.TextFormat; import flash.ui.Mouse; import flash.ui.Keyboard; import flash.events.Event; import flash.events.MouseEvent; import flash.events.KeyboardEvent; import flash.events.TimerEvent; import flash.utils.Timer; public class Main extends Sprite { // approx 67fps private var _delay:uint = 15; private var _timer:Timer; private var _ball:Ball; private var _bounce:Number = -0.3; private var _gravity:Number = 0.5; private var _friction:Number = 0.85; private var _windSpeed:Number = 0.0; private var _arrow:Arrow; private var _balls:Array; private var _can:Shape; // scoring private var _attempts:uint = 0; private var _shotsMade:uint = 0; private var _scoreText:TextField; private var _level:uint = 1; private var _won:Boolean = false; public function Main():void { init(); _timer.start(); } private function init():void { stage.scaleMode = StageScaleMode.NO_SCALE; //stage.align = StageAlign.TOP_LEFT; Mouse.hide(); // add the _arrow _arrow = addChild(new Arrow()) as Arrow; _arrow.y = stage.stageHeight; // add the trash can _can = addChild(new Shape()) as Shape; _can.graphics.lineStyle(2); _can.graphics.lineTo(0, 50); _can.graphics.moveTo(45, 0); _can.graphics.lineTo(45, 50); _can.x = stage.stageWidth - 65; _can.y = stage.stageHeight - 50; // add event listeners //stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); //addEventListener(Event.ENTER_FRAME, onEnterFrame); _timer = new Timer(_delay); _timer.addEventListener(TimerEvent.TIMER, run); // create the balls array _balls = new Array(); // add score text field var tf:TextFormat = new TextFormat("Arial", 12, 0x000000); _scoreText = addChild(new TextField) as TextField; _scoreText.defaultTextFormat = tf; _scoreText.x = 10; _scoreText.y = 30; _scoreText.width = stage.stageWidth - 10; updateScore(); // set initial rotation of arrow _arrow.rotation = -70; // add first ball _ball = addBall(); // add title var titleText:TextField = addChild(new TextField()) as TextField; titleText.defaultTextFormat = new TextFormat("Arial", 15, 0xffc20e, true, null, null, null, null, "center"); titleText.width = 200; titleText.y = 10; titleText.x = (stage.stageWidth / 2) - (titleText.width / 2); titleText.text = "Thrower"; } //private function onEnterFrame(event:Event):void { private function run(event:TimerEvent):void { for each (var ball:Ball in _balls) { updateBall(ball); } if (_won) { rotateArrow(); } } private function updateScore():void { var accuracy:Number = 0; if (_attempts > 0) { accuracy = Math.round((_shotsMade / _attempts) * 100); } _scoreText.text = _shotsMade.toString() + " out of " + _attempts + " attempts. " + accuracy.toString() + "% accuracy" + "\n\n"; if (_won) { _scoreText.appendText("C O N G R A T U L A T I O N S ! ! !\n"); _scoreText.appendText("You have unlocked the free play mode! (You can now use the mouse to rotate the arrow)"); } else { _scoreText.appendText("Level " + _level + " - Shoot the ball to advance to the next level (press+hold+release spacebar)\n"); _scoreText.appendText("Complete all 51 levels to unlock free play mode."); } } private function addBall():Ball { var ball:Ball = addChild(new Ball) as Ball; _balls.push(ball); // position the ball ball.x = _arrow.x + ball.radius; ball.y = _arrow.y - ball.radius; return ball; } private function updateBall(ball:Ball):void { if ((ball == null) || ball.stationary) { return; } if (Math.abs(ball.vx) < 0.00001) { ball.vx = 0; } // detect if ball is stationary if (ball.x == ball.x + ball.vx){ ball.stationary = true; // add new ball _ball = addBall(); if ((ball.x > _can.x) && (ball.x < _can.x + _can.width)) { // check if won the game if (!_won && (_arrow.rotation == -20)) { updateScore(); _won = true; stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); } else { // advance to next level _arrow.rotation++; _level++; } _shotsMade++; updateScore(); } } if (ball.y < stage.stageHeight / 2) { ball.vx += _windSpeed; } ball.vy += _gravity; ball.x += ball.vx; ball.y += ball.vy; var left:Number = 0; var right:Number = stage.stageWidth; var top:Number = 0; var bottom:Number = stage.stageHeight; if (_won && (ball.x + ball.radius > right)) { ball.x = right - ball.radius; ball.vx *= _bounce; } else if (_won && (ball.x - ball.radius < left)) { ball.x = left + ball.radius; ball.vx *= _bounce; } if (!_won && (ball.x - ball.radius - 5 > right)) { ball.vx = 0; } if (ball.y + ball.radius > bottom) { ball.y = bottom - ball.radius; ball.vy *= _bounce; // aplly friction when on the ground ball.vx *= _friction; } else if (_won && (ball.y - ball.radius < top)) { ball.y = top + ball.radius; ball.vy *= _bounce; } // check collision with the trash can // ball is going to the left if (ball.vx > 0) { if ((ball.x + ball.radius > _can.x) && (ball.x - ball.radius < _can.x)) { if (ball.y + ball.radius > _can.y) { if (ball.hitTestObject(_can)) { if ((ball.x > _can.x) && (ball.y < _can.y)) { ball.y = _can.y - ball.radius; ball.vy *= _bounce; } else { ball.x = _can.x - ball.radius; ball.vx *= _bounce; } } } } else if ((ball.x + ball.radius > _can.x + _can.width) && (ball.x - ball.radius < _can.x + _can.width)) { if (ball.y + ball.radius > _can.y) { if (ball.hitTestObject(_can)) { if ((ball.x > _can.x + _can.width) && (ball.y < _can.y)) { ball.y = _can.y - ball.radius; ball.vy *= _bounce; } else { ball.x = _can.x + _can.width - ball.radius; ball.vx *= _bounce; } } } } } else if (ball.vx < 0) { if ((ball.x - ball.radius < _can.x) && (ball.x + ball.radius > _can.x)) { if (ball.y + ball.radius > _can.y) { if (ball.hitTestObject(_can)) { ball.x = _can.x + ball.radius; ball.vx *= _bounce; } } } else if ((ball.x + ball.radius > _can.x + _can.width) && (ball.x - ball.radius < _can.x + _can.width)) { if (ball.y + ball.radius > _can.y) { if (ball.hitTestObject(_can)) { ball.x = _can.x + _can.width - ball.radius; ball.vx *= _bounce; } } } } } private function rotateArrow():void { var dx:Number = mouseX - _arrow.x; var dy:Number = mouseY - _arrow.y; var radians:Number = Math.atan2(dy, dx); var degrees:Number = radians * 180 / Math.PI; // limit the rotation if (Math.abs(degrees) > 20 && Math.abs(degrees) < 70) { _arrow.rotation = degrees; } } private function onMouseDown(event:MouseEvent):void { if (_ball.stationary) { startAttempt(); stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); } } private function onMouseUp(event:MouseEvent):void { endAttempt(); stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); } private function onKeyDown(event:KeyboardEvent):void { if (event.keyCode == Keyboard.SPACE) { if (_ball.stationary) { startAttempt(); stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp); } } } private function onKeyUp(event:KeyboardEvent):void { endAttempt(); stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp); } private function startAttempt():void { // start the force update on the arrow _arrow.startForceUpdate(); } private function endAttempt():void { applyForce(); // stop updating the force on the _arrow.stopForceUpdate(); _attempts++; updateScore(); } private function applyForce():void { _ball.vx = (_arrow.tip.x - _ball.x) * 0.175; _ball.vy = (_arrow.tip.y - _ball.y) * 0.175; _ball.vx *= _arrow.force / 100; _ball.vy *= _arrow.force / 100; _ball.stationary = false; } } } import flash.display.Shape; class Ball extends Shape { private const COLORS:Array = [0xffffc20e, 0xff709ad1, 0xffe5aa7a, 0xfffff200, 0xffff7e00, 0xff546d8e, 0xff9c5a3c, 0xff22b14c]; private var _radius:uint = 12; // access directly for faster performance public var stationary:Boolean = true; public var vx:Number; public var vy:Number; public function Ball():void { // choose random color var randColor:uint = Math.round(Math.random() * (COLORS.length - 1)); trace(randColor); graphics.beginFill(COLORS[randColor], 0.9); graphics.drawCircle(0, 0, _radius); graphics.endFill(); } public function get radius():uint { return _radius; } }