part of game; abstract class GameObject extends Node { GameObject(this.f); double radius = 0.0; double removeLimit = 1280.0; bool canDamageShip = true; bool canBeDamaged = true; bool canBeCollected = false; double maxDamage = 3.0; double damage = 0.0; final GameObjectFactory f; Paint _paintDebug = new Paint() ..color=new Color(0xffff0000) ..strokeWidth = 1.0 ..setStyle(sky.PaintingStyle.stroke); bool collidingWith(GameObject obj) { return (GameMath.distanceBetweenPoints(position, obj.position) < radius + obj.radius); } void move() { } void removeIfOffscreen(double scroll) { ; if (-position.y > scroll + removeLimit || -position.y < scroll - 50.0) { removeFromParent(); } } void destroy() { if (parent != null) { Explosion explo = createExplosion(); if (explo != null) { explo.position = position; parent.addChild(explo); } Collectable powerUp = createPowerUp(); if (powerUp != null) { f.addGameObject(powerUp, position); } removeFromParent(); } } void collect() { removeFromParent(); } void addDamage(double d) { if (!canBeDamaged) return; damage += d; if (damage >= maxDamage) { destroy(); f.playerState.score += (maxDamage * 10).ceil(); } } Explosion createExplosion() { return null; } Collectable createPowerUp() { return null; } void paint(PaintingCanvas canvas) { if (_drawDebug) { canvas.drawCircle(Point.origin, radius, _paintDebug); } super.paint(canvas); } void setupActions() { } } class LevelLabel extends GameObject { LevelLabel(GameObjectFactory f, int level) : super(f) { canDamageShip = false; canBeDamaged = false; Label lbl = new Label( "L E V E L $level", new TextStyle( textAlign: TextAlign.center, color:new Color(0xffffffff), fontSize: 24.0, fontWeight: FontWeight.w600 )); addChild(lbl); } } class Ship extends GameObject { Ship(GameObjectFactory f) : super(f) { // Add main ship sprite _sprt = new Sprite(f.sheet["ship.png"]); _sprt.scale = 0.3; _sprt.rotation = -90.0; addChild(_sprt); _sprtShield = new Sprite(f.sheet["shield.png"]); _sprtShield.scale = 0.35; _sprtShield.transferMode = sky.TransferMode.plus; addChild(_sprtShield); radius = 20.0; canBeDamaged = false; canDamageShip = false; // Set start position position = new Point(0.0, 50.0); } Sprite _sprt; Sprite _sprtShield; void applyThrust(Point joystickValue, double scroll) { Point oldPos = position; Point target = new Point(joystickValue.x * 160.0, joystickValue.y * 220.0 - 250.0 - scroll); double filterFactor = 0.2; position = new Point( GameMath.filter(oldPos.x, target.x, filterFactor), GameMath.filter(oldPos.y, target.y, filterFactor)); } void setupActions() { ActionTween rotate = new ActionTween((a) => _sprtShield.rotation = a, 0.0, 360.0, 1.0); _sprtShield.actions.run(new ActionRepeatForever(rotate)); } void update(double dt) { // Update shield if (f.playerState.shieldActive) { if (f.playerState.shieldDeactivating) _sprtShield.visible = !_sprtShield.visible; else _sprtShield.visible = true; } else { _sprtShield.visible = false; } } } class Laser extends GameObject { double impact = 0.0; final List<Color> laserColors = [ new Color(0xff95f4fb), new Color(0xff5bff35), new Color(0xffff886c), new Color(0xffffd012), new Color(0xfffd7fff) ]; Laser(GameObjectFactory f, int level, double r) : super(f) { // Game object properties radius = 10.0; removeLimit = _gameSizeHeight + radius; canDamageShip = false; canBeDamaged = false; impact = 1.0 + level * 0.5; // Offset for movement _offset = new Offset( math.cos(radians(r)) * 8.0, math.sin(radians(r)) * 8.0 - f.playerState.scrollSpeed); // Drawing properties rotation = r + 90.0; int numLasers = level % 3 + 1; Color laserColor = laserColors[(level ~/ 3) % laserColors.length]; // Add sprites List<Sprite> sprites = []; for (int i = 0; i < numLasers; i++) { Sprite sprt = new Sprite(f.sheet["explosion_particle.png"]); sprt.scale = 0.5; sprt.colorOverlay = laserColor; sprt.transferMode = sky.TransferMode.plus; addChild(sprt); sprites.add(sprt); } // Position the individual sprites if (numLasers == 2) { sprites[0].position = new Point(-3.0, 0.0); sprites[1].position = new Point(3.0, 0.0); } else if (numLasers == 3) { sprites[0].position = new Point(-4.0, 0.0); sprites[1].position = new Point(4.0, 0.0); sprites[2].position = new Point(0.0, -2.0); } } Offset _offset; void move() { position += _offset; } Explosion createExplosion() { return new ExplosionMini(f.sheet); } } Color colorForDamage(double damage, double maxDamage) { int alpha = ((200.0 * damage) ~/ maxDamage).clamp(0, 200); return new Color.fromARGB(alpha, 255, 3, 86); } abstract class Obstacle extends GameObject { Obstacle(GameObjectFactory f) : super(f); double explosionScale = 1.0; Explosion createExplosion() { SoundEffectPlayer.sharedInstance().play(f.sounds["explosion"]); Explosion explo = new ExplosionBig(f.sheet); explo.scale = explosionScale; return explo; } } abstract class Asteroid extends Obstacle { Asteroid(GameObjectFactory f) : super(f); Sprite _sprt; void setupActions() { // Rotate obstacle int direction = 1; if (randomBool()) direction = -1; ActionTween rotate = new ActionTween( (a) => _sprt.rotation = a, 0.0, 360.0 * direction, 5.0 + 5.0 * randomDouble()); _sprt.actions.run(new ActionRepeatForever(rotate)); } set damage(double d) { super.damage = d; _sprt.colorOverlay = colorForDamage(d, maxDamage); } Collectable createPowerUp() { return new Coin(f); } } class AsteroidBig extends Asteroid { AsteroidBig(GameObjectFactory f) : super(f) { _sprt = new Sprite(f.sheet["asteroid_big_${randomInt(3)}.png"]); _sprt.scale = 0.3; radius = 25.0; maxDamage = 5.0; addChild(_sprt); } } class AsteroidSmall extends Asteroid { AsteroidSmall(GameObjectFactory f) : super(f) { _sprt = new Sprite(f.sheet["asteroid_small_${randomInt(3)}.png"]); _sprt.scale = 0.3; radius = 12.0; maxDamage = 3.0; addChild(_sprt); } } class AsteroidPowerUp extends AsteroidBig { AsteroidPowerUp(GameObjectFactory f) : super(f); Collectable createPowerUp() { return new PowerUp(f, nextPowerUpType()); } } class EnemyScout extends Obstacle { EnemyScout(GameObjectFactory f) : super(f) { _sprt = new Sprite(f.sheet["enemy_scout_0.png"]); _sprt.scale = 0.32; radius = 12.0; maxDamage = 1.0; addChild(_sprt); constraints = [new ConstraintRotationToMovement(dampening: 0.5)]; } final double _swirlSpacing = 80.0; _addRandomSquare(List<Offset> offsets, double x, double y) { double xMove = (randomBool()) ? _swirlSpacing : -_swirlSpacing; double yMove = (randomBool()) ? _swirlSpacing : -_swirlSpacing; if (randomBool()) { offsets.addAll([ new Offset(x, y), new Offset(xMove + x, y), new Offset(xMove + x, yMove + y), new Offset(x, yMove + y), new Offset(x, y) ]); } else { offsets.addAll([ new Offset(x, y), new Offset(x, y + yMove), new Offset(xMove + x, yMove + y), new Offset(xMove + x, y), new Offset(x, y) ]); } } void setupActions() { List<Offset> offsets = []; _addRandomSquare(offsets, -_swirlSpacing, 0.0); _addRandomSquare(offsets, _swirlSpacing, 0.0); offsets.add(new Offset(-_swirlSpacing, 0.0)); List<Point> points = []; for (Offset offset in offsets) { points.add(position + offset); } ActionSpline spline = new ActionSpline((a) => position = a, points, 6.0); spline.tension = 0.7; actions.run(new ActionRepeatForever(spline)); } Collectable createPowerUp() { return new Coin(f); } Sprite _sprt; } class EnemyDestroyer extends Obstacle { EnemyDestroyer(GameObjectFactory f) : super(f) { _sprt = new Sprite(f.sheet["enemy_destroyer_1.png"]); _sprt.scale = 0.32; radius = 24.0; maxDamage = 4.0; addChild(_sprt); constraints = [new ConstraintRotationToNode(f.level.ship, dampening: 0.05)]; } int _countDown = randomInt(120) + 240; void setupActions() { ActionCircularMove circle = new ActionCircularMove( (a) => position = a, position, 40.0, 360.0 * randomDouble(), randomBool(), 3.0); actions.run(new ActionRepeatForever(circle)); } Collectable createPowerUp() { return new Coin(f); } void update(double dt) { _countDown -= 1; if (_countDown <= 0) { // Shoot at player EnemyLaser laser = new EnemyLaser(f, rotation, 5.0, new Color(0xffffe38e)); laser.position = position; f.level.addChild(laser); _countDown = 60 + randomInt(120); } } set damage(double d) { super.damage = d; _sprt.colorOverlay = colorForDamage(d, maxDamage); } Sprite _sprt; } class EnemyLaser extends Obstacle { EnemyLaser(GameObjectFactory f, double rotation, double speed, Color color) : super(f) { _sprt = new Sprite(f.sheet["explosion_particle.png"]); _sprt.scale = 0.5; _sprt.rotation = rotation + 90; _sprt.colorOverlay = color; addChild(_sprt); canDamageShip = true; canBeDamaged = false; double rad = radians(rotation); _movement = new Offset(math.cos(rad) * speed, math.sin(rad) * speed); } Sprite _sprt; Offset _movement; void move() { position += _movement; } } class EnemyBoss extends Obstacle { EnemyBoss(GameObjectFactory f) : super(f) { radius = 48.0; _sprt = new Sprite(f.sheet["enemy_destroyer_1.png"]); _sprt.scale = 0.64; addChild(_sprt); maxDamage = 40.0; constraints = [new ConstraintRotationToNode(f.level.ship, dampening: 0.05)]; _powerBar = new PowerBar(new Size(60.0, 10.0)); _powerBar.pivot = new Point(0.5, 0.5); f.level.addChild(_powerBar); _powerBar.constraints = [new ConstraintPositionToNode( this, dampening: 0.5, offset: new Offset(0.0, -70.0) )]; } Sprite _sprt; PowerBar _powerBar; int _countDown = randomInt(120) + 240; void update(double dt) { _countDown -= 1; if (_countDown <= 0) { // Shoot at player fire(10.0); fire(0.0); fire(-10.0); _countDown = 60 + randomInt(120); } } void fire(double r) { r += rotation; EnemyLaser laser = new EnemyLaser(f, r, 5.0, new Color(0xffffe38e)); double rad = radians(r); Offset startOffset = new Offset(math.cos(rad) * 30.0, math.sin(rad) * 30.0); laser.position = position + startOffset; f.level.addChild(laser); } void setupActions() { ActionOscillate oscillate = new ActionOscillate((a) => position = a, position, 120.0, 3.0); actions.run(new ActionRepeatForever(oscillate)); } void destroy() { f.playerState.boss = null; _powerBar.removeFromParent(); // Flash the screen NodeWithSize screen = f.playerState.parent; screen.addChild(new Flash(screen.size, 1.0)); super.destroy(); // Add coins for (int i = 0; i < 20; i++) { Coin coin = new Coin(f); Point pos = new Point( randomSignedDouble() * 160, position.y + randomSignedDouble() * 160.0); f.addGameObject(coin, pos); } } Explosion createExplosion() { ExplosionBig explo = new ExplosionBig(f.sheet); explo.scale = 1.5; return explo; } set damage(double d) { super.damage = d; _sprt.actions.stopAll(); _sprt.actions.run(new ActionTween( (a) =>_sprt.colorOverlay = a, new Color.fromARGB(180, 255, 3, 86), new Color(0x00000000), 0.3 )); _powerBar.power = (1.0 - (damage / maxDamage)).clamp(0.0, 1.0); } } class Collectable extends GameObject { Collectable(GameObjectFactory f) : super(f) { canDamageShip = false; canBeDamaged = false; canBeCollected = true; zPosition = 20.0; } } class Coin extends Collectable { Coin(GameObjectFactory f) : super(f) { _sprt = new Sprite(f.sheet["coin.png"]); _sprt.scale = 0.7; addChild(_sprt); radius = 7.5; } void setupActions() { // Rotate ActionTween rotate = new ActionTween((a) => _sprt.rotation = a, 0.0, 360.0, 1.0); actions.run(new ActionRepeatForever(rotate)); // Fade in ActionTween fadeIn = new ActionTween((a) => _sprt.opacity = a, 0.0, 1.0, 0.6); actions.run(fadeIn); } Sprite _sprt; void collect() { f.playerState.addCoin(this); super.collect(); } } enum PowerUpType { shield, speedLaser, sideLaser, speedBoost, } List<PowerUpType> _powerUpTypes = new List.from(PowerUpType.values); int _lastPowerUp = _powerUpTypes.length; PowerUpType nextPowerUpType() { if (_lastPowerUp >= _powerUpTypes.length) { _powerUpTypes.shuffle(); _lastPowerUp = 0; } PowerUpType type = _powerUpTypes[_lastPowerUp]; _lastPowerUp++; return type; } class PowerUp extends Collectable { PowerUp(GameObjectFactory f, this.type) : super(f) { _sprt = new Sprite(f.sheet["coin.png"]); _sprt.scale = 1.2; addChild(_sprt); radius = 10.0; } Sprite _sprt; PowerUpType type; void setupActions() { ActionTween rotate = new ActionTween((a) => _sprt.rotation = a, 0.0, 360.0, 1.0); actions.run(new ActionRepeatForever(rotate)); // Fade in ActionTween fadeIn = new ActionTween((a) => _sprt.opacity = a, 0.0, 1.0, 0.6); actions.run(fadeIn); } void collect() { f.playerState.activatePowerUp(type); super.collect(); } }