class Ship : AbstractRole, Human, Bounces { // "@Attribute" means that these attributes can be changed in the Scene Editor. // The center of the planet/moon @Attribute val center = Vector2( 320, -1000 ) // Is the ship pointing INWARDS towards the center // First used by scene moon1, where the moon is in the distance. @Attribute var inwards = false // The speed the ship moves (in degrees, as we move in circles) @Attribute var speedDegrees = 0.2 // The speed of our bullets @Attribute var bulletSpeed = 6 @Attribute var shieldTicks = 1000 @Attribute var canMoveShielded = false @Attribute( rows=4 ) var message = "" var orbit = 0 // Distance from the center of the planet/moon. Set in activated() var radius = 26 // The size of the ship (use when checking if the ship tries to move off-screen). var shieldUsed = 0 // Number of "ticks" that the shield has been used. var shielded = false // Is the shield on? // Keyboard inputs. Set in activated() method. // Note, the keys used are defined in the "inputs" section from within the Tickle editor. // Note, more than one key can be assigned to each input. var left : Input // Left arrow key, or"A" var right : Input // Right arrow key or"D" var fire : Input // Space, Return, Up arrow key or "W" var shield : Input // Ctrl, Shift, Down arrow key or "S" - take your pick! // Our bullet's Actor (not the Role). // Set in fire(), and set back to null in tick(), after the bullet has died // (moved off screen, or hit something) var bulletA : Actor = null // Called when the scene first starts. We initialise stuff here. override fun activated() { val inputs = Game.instance.resources.inputs left = inputs.find("left") right = inputs.find("right") fire = inputs.find("fire") shield = inputs.find("shield") orbit = actor.position.distance( center ) if(inwards) { orbit = -orbit } actor.direction.radians = if (inwards ) Angle.radiansOf( center, actor.position ) else Angle.radiansOf( actor.position, center ) if (message!= "") { Talk.talk( actor, message, 3 ) } } // Called 60 times per second. Check the keyboard inputs, and act appropriately. override fun tick() { if (shield.isPressed() && shieldUsed < shieldTicks) { shieldUsed ++ if ( shieldUsed >= shieldTicks ) { shielded = false } else { shielded = true } choosePose() } else { if (shielded) { shielded = false choosePose() } } if ( ! shielded || canMoveShielded ) { if (left.isPressed()) { rotate( speedDegrees ) } if (right.isPressed()) { rotate( -speedDegrees ) } } // Have we fired a bullet? (Only one human bullet allowed on screen at a time!) if ( bulletA != null ) { if ( bulletA.stage == null ) { // If the bullet we fired is destroyed, then reload, ready to fire again. bulletA = null choosePose() } } else { if (fire.isPressed() ) { actor.event( "fire" ) bulletA = actor.createChild("bullet") bulletA.direction = actor.direction bulletA.moveForwards( 16 ) (bulletA.role as Bullet).speed = bulletSpeed // Slow bullets are large, fast bullets are small bulletA.scaleXY = 12 / bulletSpeed // Change pose (to show we are reloading (and cannot fire yet) choosePose() } } } // Changes the ships appearance. // If shielded, pick the pose to indicate the shield level ("shield") // Otherwise, show if the ship is ready to fire or not ("reloaded" or "reloading") fun choosePose() { if (shielded) { val n = (8 * shieldUsed)~/shieldTicks actor.event( "shield$n" ) } else { if ( bulletA == null ) { // Note, this event could also have a sound associated with it, so // we can HEAR that we've reloaded. actor.event( "reloaded" ) } else { actor.event( "reloading" ) } } } // Move the ship in a circle (around the planet/moon) fun rotate( degrees : double ) { actor.direction.degrees += degrees actor.position.set( center ) actor.position += ( actor.direction.vector() * orbit ) // If rotated too far, reset to the previous position if ( (actor.x < radius) || (actor.x > 640 - radius) || (actor.y < radius) || (actor.y > 480 - radius) ) { actor.direction.degrees -= degrees actor.position.set( center ) actor.position += ( actor.direction.vector() * orbit ) } } // Called when hit by an AlienBullet, or an AlienShip override fun hit() { actor.event("die") actor.role = DyingWithShrapnel(100) // Make a "shake" effect, by moving a screenshot rapidly left and right DrunkInvaders.instance.shake(1) // If there were more than one ship, then, just let this one die, and carry on with // the other one! if ( actor.stage.findRoleByClass( Ship ) == null ) { val lives = actor.stage.findRolesByClass( Life ) if ( lives.isEmpty() ) { // Game over! } else { val life = lives[lives.size()-1] life.rebirth( this ) DrunkInvaders.instance.lives -- } } } // An alien ship has hit us while our shield is on. It will bounce away harmlessly. // We don't need to do anything, but could make a sound, or talk, reduce our shield etc. override fun bounce( impact : double ) { } }