package { import flash.display.MovieClip; import flash.geom.Point; import flash.events.Event; import flash.events.TimerEvent; import flash.utils.Timer; public class AIShip extends Ship { private var id:int; private var sector:Sector; private var target:Ship; private var firing:Boolean = false; private var fireCounter:int = 0; private var mainWep:Object; private var secWep:Object; private var aggro_gain:int = 800; private var aggro_loose:int = 800; private var behavior_prim:int = Behaviors.Guard; private var guardSpot:Point; //private var line:Border = new Border();//debug private var avoidingObject:Boolean = false; private var secFireLoaded:Boolean = true; private var secFireTimer:Timer; public function AIShip($ID:int, $coords:Point, $rotation:Number, $allegience:int, $sector:Sector, $shipData:Object, $weaponData:Object) { id = $ID; sector = $sector; mainWep = $weaponData.getWeaponData($shipData.weaponPrim); secWep = $weaponData.getWeaponData($shipData.weaponSec); $shipData.weaponSecAmmo = 1;//ai ship only get 1 secAmmo, this "hack" only works because DBShipData creates new $shipData for every ship super($coords, $rotation, $sector, $shipData, $allegience); guardSpot = new Point($coords.x, $coords.y); secFireTimer = new Timer(secWep.rof * 2);//ai ships have 2x reload time secFireTimer.addEventListener(TimerEvent.TIMER, reEnableSecFire, false, 0, true); //loop addEventListener(Event.ENTER_FRAME, loop); //line.scaleY = 5;//debug //line.scaleX = .5;//debug //line.y = -(line.height / 2);//debug //addChild(line);//debug } //--Main loop-- private function loop($e:Event):void { move(); checkFuturePos(); //Firing loop if (++fireCounter >= (40 / mainWep.rof))// 40 = the framerate { fireCounter = 0; fire(); } //Is target dead? if (target && target.HP <= 0) { target = null; } //Primary behaviors switch (behavior_prim) { case Behaviors.Guard: behavior_guard(); break; } } //--Hits-- public override function projectileHit($damage:Number, $firedFrom:Ship):void { if (!target) target = $firedFrom;//aggro the ship who fired the projectile super.projectileHit($damage, $firedFrom); } //--Movement-- private function getFuturePos():Point { return new Point( x + (SpeedX * 10), y + (SpeedY * 10) ); } ///find out if there's something in the way private function checkFuturePos():void { if (asteroidHitCheckAt(getFuturePos()) || isOutside(getFuturePos()))//there's something in the way, turn away from it { avoidingObject = true; firing = false; if (findTurnAngle() > 0) turnCW(); else turnCCW(); if (Speed > (SpeedMax * .2)) slowDown(); else speedUp(); //line.rotation = findTurnAngle();//debug } else { avoidingObject = false; } } ///finds the shortest direction to turn away from an object private function findTurnAngle():int { var tempPos:Point; var cw:int; var ccw:int; //clock-wise for (var i:int = 0; i < 180; i++) { tempPos = new Point( x + Math.sin(Globals.degToRad(rotation + i)) * (Speed * 10), y + Math.cos(Globals.degToRad(rotation + i)) * ( -Speed * 10) ); if (!asteroidHitCheckAt(tempPos) && !isOutside(tempPos)) { cw = i; i = 360;//ends the for-loop } } //counter clock-wise for (i = 0; i > -180; i--) { tempPos = new Point( x + Math.sin(Globals.degToRad(rotation + i)) * (Speed * 10), y + Math.cos(Globals.degToRad(rotation + i)) * ( -Speed * 10) ); if (!asteroidHitCheckAt(tempPos) && !isOutside(tempPos)) { ccw = i; i = -180;//ends the for-loop } } if (cw > Math.abs(ccw)) return ccw; else return cw; } private function speedUp():void { Speed += SpeedAcc; } private function slowDown():void { Speed -= BreakSpeed; } private function turnCCW():void { TorqueSpeed -= TorqueAcc; } private function turnCW():void { TorqueSpeed += TorqueAcc; } private function stopTurning():void { if (Math.abs(TorqueSpeed) <= .5) TorqueSpeed = 0; else TorqueSpeed *= .8; } //Goto private function goto($spot:Point):void//fly to a place and stop { var turningDir:Number = Globals.turningDir(guardSpot.x * 32, guardSpot.y * 32, x, y, rotation); if (Globals.calcDist(guardSpot.x * 32, guardSpot.y * 32, x, y) > 120 && !avoidingObject) { speedUp(); //Turning if (Math.abs(turningDir) > 10) { if (turningDir > 0) turnCW(); else turnCCW(); } else { stopTurning(); } } else if (!avoidingObject) { slowDown(); stopTurning(); } } //--Behaviors-- //Guard private function behavior_guard():void { if (target)//if a target was found { hunt(); } else //if a target wasn't found { firing = false; goto(guardSpot); target = lookForTarget(); } } //--Other AI-stuff-- ///makes passes and attacks target private function hunt():void { //collect info about target var targetAngle:Number = Math.abs(Globals.turningDir(target.x, target.y, x, y, rotation)); var targetDist:Number = Globals.calcDist(target.x, target.y, x, y); if ((targetDist > 300 || targetAngle < 90) && targetDist > 200 && !avoidingObject)//(far away or behind me) && not too close { speedUp(); var turningDir:Number = Globals.turningDir(target.x, target.y, x, y, rotation); if (Math.abs(turningDir) < 10)//target within the fire cone { stopTurning(); firing = true; //fire missle when target isn't turning if (target.TorqueSpeed < .1) fireMissle(); } else //target is not within the fire cone { if (turningDir > 0) turnCW(); else turnCCW(); firing = false; } } else if(!avoidingObject)//evades target until it's some distance away from it, then it continues attacking again { evade(target); } } //Evade private function evade($evadeTarget:MovieClip):void//run away from target { speedUp(); firing = false; var turningDir:Number = Globals.turningDir($evadeTarget.x, $evadeTarget.y, x, y, rotation); turningDir += 180; if (Math.abs(turningDir) > 30) { if (turningDir > 0) turnCW(); else turnCCW(); } else { stopTurning(); } } //--Misc-- //Look for target private function lookForTarget():Ship { var ships:Array = sector.getShips(); for (var i:int = 0; i < ships.length; i++ ) { if (ships[i] && ships[i].Allegiance != Allegiance) { var distance:Number = Globals.calcDist(ships[i].x, ships[i].y, x, y); if (distance <= aggro_gain) return ships[i]; } } return null; } //Fire private function fire():void { if (firing && HP > 0) { getSector.addChildAt(new Projectile(Speed, rotation, mainWep, getSector, this), 0); } } private function fireMissle():void { if (secFireLoaded && SecAmmo > 0) { getSector.addChildAt(new Missile(Speed, rotation, secWep, getSector, this), 0); secFireLoaded = false; SecAmmo--; secFireTimer.start(); } } private function reEnableSecFire($e:Event):void { secFireTimer.reset(); secFireLoaded = true; } //Destroy public override function destroy():void { HP = 0; Dying = true; target = null; getSector.removeAIShip(id); removeEventListener(Event.ENTER_FRAME, loop); super.destroy(); } //--------------------------------------------set/get-------------------------------------------- public override function get ID():int { return id; } } }