package spaceInvaders; import f3.ui.*; import f3.ui.canvas.*; import java.lang.System; import java.awt.Rectangle; import java.lang.Math; import java.awt.Point; class Game { attribute shipIconUrl:String; attribute alienIconUrl:String; attribute missleIconUrl:String; attribute alienX: Number; attribute alienY: Number; attribute aliens: Alien*; attribute livingAliens: Number; attribute missles: Missle*; attribute alienMissles: Missle*; attribute ship: Ship; operation moveShipTo(x: Number); operation fireMissle(); operation fireAlienMissle(alien:Alien); operation moveAliens(); operation play(); } attribute Game.ship = Ship { x: 800/2, y: 530 }; attribute Game.alienIconUrl = "{__DIR__}/images/alien.gif"; attribute Game.shipIconUrl = "{__DIR__}/images/ship.gif"; attribute Game.missleIconUrl = "{__DIR__}/images/shot.gif"; operation Game.moveShipTo(x: Number) { if (ship.exploding or ship.exploded) { return; } x = Math.max(x, 5); x = Math.min(x, 800-5); ship.x = x; } operation Game.fireMissle() { if (ship.exploded) { return; } if (false and sizeof missles > 3) { return; } var missle = new Missle(); missle.x = ship.x + 10.5; missle.y = ship.y - 25; insert missle into missles; for (i in [missle.y.intValue()..0]) (dur 1000 linear fps 40 while not missle.exploded) { missle.y = i; var p = new Point(missle.x, missle.y); for (alien in reverse aliens[not dead]) { var rect = new Rectangle(alienX + alien.column*53, alienY + alien.row *35, 43, 29); if (rect.contains(p)) { alien.exploding = [true, false] dur 500; alien.dead = true; livingAliens--; missle.exploded = true; delete missles[. == missle]; break; } } if (i == 0) { delete missles[. == missle]; } } } operation Game.fireAlienMissle(alien:Alien) { if (alien.dead) { return; } var missle = new Missle(); missle.x = alienX + alien.column * 53 + 43/2 + 5; missle.y = alienY + alien.row * 35 + 29/2; insert missle into alienMissles; var start = missle.y.intValue(); var delta = 600 - start; for (unitinterval r in dur 1000 fps 40 while not missle.exploded) { missle.y = start + r * delta; var shipRect = new Rectangle(ship.x, ship.y, 33, 23); if (shipRect.contains(new Point(missle.x, missle.y))) { ship.exploding = [true, false] dur 3000; ship.exploded = true; missle.exploded = true; delete alienMissles[. == missle]; } if (r == 1) { delete alienMissles[. == missle]; } } } trigger on Game.alienX = x { if (x == 0 or x == 150) { var toValue = alienY + 35; var start = alienY[0]; var delta = toValue - start; for (unitinterval r in dur 1000 fps 40 while not ship.exploded and livingAliens > 0) { alienY = start + r*35; if (r == 1) { this.moveAliens(); } var rand = (Math.random() *100) % sizeof aliens; do later { this.fireAlienMissle(aliens[rand]); } if (alienY + 5 * 35 >= ship.y) { var shipRect = new Rectangle(ship.x, ship.y, 33, 23); for (alien in reverse aliens[not dead]) { var rect = new Rectangle(alienX + alien.column*53, alienY + alien.row *35, 53, 35); if (rect.intersects(shipRect) or rect.y + rect.height >= shipRect.y + shipRect.height) { ship.exploded = true; ship.exploding = [true, false] dur 1500; if (rect.intersects(shipRect)) { alien.exploding = [true, false] dur 500; alien.dead = true; } break; } } } } } } operation Game.moveAliens() { if (ship.exploded or livingAliens == 0) { return; } if (alienX == 0) { for (i in [0..150]) (dur 3000 linear while not ship.exploded and livingAliens > 0) { alienX = i; } } else { for (i in [150..0]) (dur 3000 linear while not ship.exploded and livingAliens > 0) { alienX = i; } } } operation Game.play() { alienX = 0; alienY = 0; ship.exploded = false; //ship.x = 800/2; if (sizeof aliens == 0) { aliens = foreach (row in [0..4], column in [0..11]) Alien { row: row column: column }; } else { aliens.dead = false; } livingAliens = sizeof aliens; this.moveAliens(); } var game = new Game(); Frame { width: 800 height: 700 onClose: operation() { System.exit(0); } title: "F3 Space Invaders" visible: true var alienImage = Image {url: game.alienIconUrl} var explosion = Image {url: "{__DIR__}/images/explosion.gif"} content: BorderPanel { top: ToolBar { buttons: [Button { text: "Play again?" enabled: bind game.ship.exploded or game.livingAliens == 0 action: operation() {game.play();} }, Button { text: "Too much for you?" action: operation() {System.exit(0);} }] } center: Canvas { background: black content: Group { content: [Group { // Aliens transform: bind translate(game.alienX, game.alienY) var alienGrid = foreach (row in [0..4], column in [0..11]) new Point(column *53, row *35) content: bind foreach (alien in game.aliens) Group { content: bind if (alien.exploding) then Group { content: [ImageView { transform: translate(alienGrid[indexof alien].x,alienGrid[indexof alien].y) image: explosion }, Text { x: -20 y: -20 transform: [translate(alienGrid[indexof alien].x, alienGrid[indexof alien].y), rotate(-12, 0, 0)] fill: white font: new Font("Verdana", "BOLD", 20) content: "200" }] } else if (alien.dead) then null else ImageView { transform: translate(alienGrid[indexof alien].x, alienGrid[indexof alien].y) image: alienImage } } }, Group { // ship content: bind if (game.ship.exploding or game.ship.exploded) then Group { content: [ImageView { transform: bind translate(game.ship.x, game.ship.y) image: explosion }, Text { x: -130 y: -30 fill: orange transform: bind [translate(game.ship.x, game.ship.y), rotate(-12, 0, 0)] font: new Font("Verdana", "BOLD", 24) stroke: red strokeWidth: 1 fill: yellow content: "You're still a winner to me" }] } else if (game.ship.exploded) then null else ImageView { transform: bind translate(game.ship.x, game.ship.y) image: {url: game.shipIconUrl} } }, Group { // missles fired from ship var missleImage = Image {url: game.missleIconUrl} content: bind foreach (missle in game.missles) ImageView { transform: bind translate(missle.x, missle.y) image: missleImage } }, Group { // missles fired by aliens var alienMissleImage = Image {url: game.missleIconUrl} var rotatedImage = CanvasImage { var im = ImageView { image: alienMissleImage } content: Group { transform: bind rotate(180, im.currentWidth/2, im.currentHeight/2) content: im } } content: bind foreach (missle in game.alienMissles) Group { transform: bind translate(missle.x, missle.y) content: ImageView { image: rotatedImage } } }, Text { // Message when you win x: 140 y: 200 font: new Font("Verdana", "BOLD", 36) fill: orange visible: bind game.livingAliens == 0 content: "Congratulations, You Win!" }, View { // Score transform: translate(10, 570) content: Label { foreground: white text: bind "
Score: {(60 - game.livingAliens)*200 format as <<0>>}
" } }, Rect { // Transparent surface for mouse events width: 800 height: 600 fill: new Color(1, 1, 1, 0), onMouseMoved: operation(e:CanvasMouseEvent) { game.moveShipTo(e.localX-15); } onMouseClicked: operation(e) { game.fireMissle(); } }] } } } } game.play();