// AlienShips can be placed in a Scene individually, or created on-mass by AlienFactory. // They can also be created from a Mothership. // // There are three different Costumes, which use this Role. In addition, Mothership // extends AlienShip. It behaves the same, but also gives birth to more AlienShips. // // The move and spin, and bounce off the edges of the screen, other AlienShips and Shields. // They also bounce off of Ship when it is shielded. // The direction of movement is unrelated to the angle of their image (they are drunk!). // // AlienBullets are fired at random intervals. class AlienShip : AbstractRole, Alien, Bounces { @Attribute var fireFrequency = 100 @Attribute val velocity = Vector2( 0, 0 ) @Attribute var spin = 0.0 @Attribute var bulletSpeed = 3 // Points awarded when this AlienShip is shot. // By used "@CostumeAttribute", the 3 different alien types can be assigned a // different number of points in the Editor's Costume. @CostumeAttribute var points = 10 // Used as a rough indication of size, when bouncing off the edges of the screen. // It is NOT used when colliding with other Actors. @CostumeAttribute var radius = 16 // Used when colliding with other AlienShips. @CostumeAttribute var mass = 1.0 // private attributes // Changes the image size. // Only used when an AlienShip is created from a MotherShip. var scaleAction : Action = null // An Action, which cycles through the frames (i.e. changing pose) var animation : Action = null override fun activated() { Level.instance.newAlien() animation = createAnimation().forever() animation.begin() } fun createAnimation() : Action { return Delay( 0.2 ) then EventAction( actor, "frame1" ) then Delay( 0.2 ) then EventAction( actor, "default" ) } override fun tick() { animation.act() if ( scaleAction != null ) { if (scaleAction.act()) { scaleAction = null } } actor.position += velocity if ( spin > 5 ) spin = 5 if ( spin < -5) spin = -5 actor.direction.degrees += spin if ( actor.x > 640 - radius && velocity.x > 0 ) { velocity.x = - velocity.x } if ( actor.x < radius && velocity.x < 0 ) { velocity.x = - velocity.x } if ( actor.y > 480 - radius && velocity.y > 0 ) { velocity.y = - velocity.y } if ( actor.y < radius && velocity.y < 0 ) { velocity.y = - velocity.y } if ( Rand.oneIn( fireFrequency ) ) { fire() } // Get the collision algorithm, shared throughout the game. val overlapping = DrunkInvaders.instance.overlapping // Look for other AlienShips, Shields and Ships (all implement Bounces) for( other in actor.stage.findRolesByClass( Bounces ) ) { if ( other != this && overlapping.overlapping( actor, other.actor ) ) { // Unshielded ships must be handled differently. if (other is Ship && !(other as Ship).shielded) { (other as Ship).hit() } else { bounceWith( other ) } } } } fun bounceWith( other : Bounces ) { val otherVelocity = Vector2(0,0) var otherMass : double = 1.0 // Only AlienShips have a velocity and mass (Ship and Shield don't), // so we need to treat them differently. if ( other is AlienShip ) { otherVelocity.set((other as AlienShip).velocity ) otherMass = (other as AlienShip).mass } else { otherVelocity.set( 0,0 ) // Assume they aren't moving otherMass = 1000 // Very heavy, so the AliedShip bouces away } // Collides two obects, as if they were perfectly round, without friction. // The resulting "impact" is high for fast, head-on collisions, and low for // slow or glancing collisions. val impact = CircularCollision.collision( actor.position, velocity, mass, other.actor.position, otherVelocity, otherMass ) // Changes the rate we are spinning by a random amount // This isn't anything like physically correct! However, it make the game look good ;-) spin += Rand.plusMinus( impact ) // Let the other guy know that it has been bounced (it may emit a sound, or change spin etc). other.bounce(impact) } // Called when another AlienShip bounces against this one. override fun bounce( impact : double ) { spin += Rand.plusMinus( impact ) } fun fire() { val bulletA = actor.createChild( "bullet" ) bulletA.direction.radians = actor.direction.radians bulletA.moveForwards( 10 ) (bulletA.role as AlienBullet).speed = bulletSpeed } override fun hit() { if ( actor.scaleXY > 1 ) { scaleAction = ScaleTo( actor, 1, actor.scaleXY * 0.9, Eases.easeInOut ) scaleAction.begin() } else { actor.event( "die" ) Level.instance.alienDied() DrunkInvaders.instance.score += points actor.role = DyingWithShrapnel(6) } } }