597 lines
14 KiB
Dart
597 lines
14 KiB
Dart
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
|
|
..style = ui.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
|
|
_sprite = new Sprite(f.sheet["ship.png"]);
|
|
_sprite.scale = 0.3;
|
|
_sprite.rotation = -90.0;
|
|
addChild(_sprite);
|
|
|
|
_spriteShield = new Sprite(f.sheet["shield.png"]);
|
|
_spriteShield.scale = 0.35;
|
|
_spriteShield.transferMode = ui.TransferMode.plus;
|
|
addChild(_spriteShield);
|
|
|
|
radius = 20.0;
|
|
canBeDamaged = false;
|
|
canDamageShip = false;
|
|
|
|
// Set start position
|
|
position = new Point(0.0, 50.0);
|
|
}
|
|
|
|
Sprite _sprite;
|
|
Sprite _spriteShield;
|
|
|
|
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((double a) { _spriteShield.rotation = a; }, 0.0, 360.0, 1.0);
|
|
_spriteShield.actions.run(new ActionRepeatForever(rotate));
|
|
}
|
|
|
|
void update(double dt) {
|
|
// Update shield
|
|
if (f.playerState.shieldActive) {
|
|
if (f.playerState.shieldDeactivating)
|
|
_spriteShield.visible = !_spriteShield.visible;
|
|
else
|
|
_spriteShield.visible = true;
|
|
} else {
|
|
_spriteShield.visible = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
class Laser extends GameObject {
|
|
double impact = 0.0;
|
|
|
|
final List<Color> laserColors = <Color>[
|
|
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 = <Sprite>[];
|
|
for (int i = 0; i < numLasers; i++) {
|
|
Sprite sprite = new Sprite(f.sheet["explosion_particle.png"]);
|
|
sprite.scale = 0.5;
|
|
sprite.colorOverlay = laserColor;
|
|
sprite.transferMode = ui.TransferMode.plus;
|
|
addChild(sprite);
|
|
sprites.add(sprite);
|
|
}
|
|
|
|
// 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 _sprite;
|
|
|
|
void setupActions() {
|
|
// Rotate obstacle
|
|
int direction = 1;
|
|
if (randomBool()) direction = -1;
|
|
ActionTween rotate = new ActionTween(
|
|
(double a) { _sprite.rotation = a; },
|
|
0.0, 360.0 * direction, 5.0 + 5.0 * randomDouble());
|
|
_sprite.actions.run(new ActionRepeatForever(rotate));
|
|
}
|
|
|
|
set damage(double d) {
|
|
super.damage = d;
|
|
_sprite.colorOverlay = colorForDamage(d, maxDamage);
|
|
}
|
|
|
|
Collectable createPowerUp() {
|
|
return new Coin(f);
|
|
}
|
|
}
|
|
|
|
class AsteroidBig extends Asteroid {
|
|
AsteroidBig(GameObjectFactory f) : super(f) {
|
|
_sprite = new Sprite(f.sheet["asteroid_big_${randomInt(3)}.png"]);
|
|
_sprite.scale = 0.3;
|
|
radius = 25.0;
|
|
maxDamage = 5.0;
|
|
addChild(_sprite);
|
|
}
|
|
}
|
|
|
|
class AsteroidSmall extends Asteroid {
|
|
AsteroidSmall(GameObjectFactory f) : super(f) {
|
|
_sprite = new Sprite(f.sheet["asteroid_small_${randomInt(3)}.png"]);
|
|
_sprite.scale = 0.3;
|
|
radius = 12.0;
|
|
maxDamage = 3.0;
|
|
addChild(_sprite);
|
|
}
|
|
}
|
|
|
|
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) {
|
|
_sprite = new Sprite(f.sheet["enemy_scout_0.png"]);
|
|
_sprite.scale = 0.32;
|
|
radius = 12.0;
|
|
maxDamage = 1.0;
|
|
addChild(_sprite);
|
|
|
|
constraints = <Constraint>[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(<Offset>[
|
|
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(<Offset>[
|
|
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 = <Offset>[];
|
|
_addRandomSquare(offsets, -_swirlSpacing, 0.0);
|
|
_addRandomSquare(offsets, _swirlSpacing, 0.0);
|
|
offsets.add(new Offset(-_swirlSpacing, 0.0));
|
|
|
|
List<Point> points = <Point>[];
|
|
for (Offset offset in offsets) {
|
|
points.add(position + offset);
|
|
}
|
|
|
|
ActionSpline spline = new ActionSpline((Point a) => position = a, points, 6.0);
|
|
spline.tension = 0.7;
|
|
actions.run(new ActionRepeatForever(spline));
|
|
}
|
|
|
|
Collectable createPowerUp() {
|
|
return new Coin(f);
|
|
}
|
|
|
|
Sprite _sprite;
|
|
}
|
|
|
|
class EnemyDestroyer extends Obstacle {
|
|
EnemyDestroyer(GameObjectFactory f) : super(f) {
|
|
_sprite = new Sprite(f.sheet["enemy_destroyer_1.png"]);
|
|
_sprite.scale = 0.32;
|
|
radius = 24.0;
|
|
maxDamage = 4.0;
|
|
addChild(_sprite);
|
|
|
|
constraints = <Constraint>[new ConstraintRotationToNode(f.level.ship, dampening: 0.05)];
|
|
}
|
|
|
|
int _countDown = randomInt(120) + 240;
|
|
|
|
void setupActions() {
|
|
ActionCircularMove circle = new ActionCircularMove(
|
|
(Point 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;
|
|
_sprite.colorOverlay = colorForDamage(d, maxDamage);
|
|
}
|
|
|
|
Sprite _sprite;
|
|
}
|
|
|
|
class EnemyLaser extends Obstacle {
|
|
EnemyLaser(GameObjectFactory f, double rotation, double speed, Color color) : super(f) {
|
|
_sprite = new Sprite(f.sheet["explosion_particle.png"]);
|
|
_sprite.scale = 0.5;
|
|
_sprite.rotation = rotation + 90;
|
|
_sprite.colorOverlay = color;
|
|
addChild(_sprite);
|
|
|
|
canDamageShip = true;
|
|
canBeDamaged = false;
|
|
|
|
double rad = radians(rotation);
|
|
_movement = new Offset(math.cos(rad) * speed, math.sin(rad) * speed);
|
|
}
|
|
|
|
Sprite _sprite;
|
|
Offset _movement;
|
|
|
|
void move() {
|
|
position += _movement;
|
|
}
|
|
}
|
|
|
|
class EnemyBoss extends Obstacle {
|
|
EnemyBoss(GameObjectFactory f) : super(f) {
|
|
radius = 48.0;
|
|
_sprite = new Sprite(f.sheet["enemy_destroyer_1.png"]);
|
|
_sprite.scale = 0.64;
|
|
addChild(_sprite);
|
|
maxDamage = 40.0;
|
|
|
|
constraints = <Constraint>[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 = <Constraint>[new ConstraintPositionToNode(
|
|
this,
|
|
dampening: 0.5,
|
|
offset: new Offset(0.0, -70.0)
|
|
)];
|
|
}
|
|
|
|
Sprite _sprite;
|
|
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((Point 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;
|
|
_sprite.actions.stopAll();
|
|
_sprite.actions.run(new ActionTween(
|
|
(Color a) { _sprite.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) {
|
|
_sprite = new Sprite(f.sheet["coin.png"]);
|
|
_sprite.scale = 0.7;
|
|
addChild(_sprite);
|
|
|
|
radius = 7.5;
|
|
}
|
|
|
|
void setupActions() {
|
|
// Rotate
|
|
ActionTween rotate = new ActionTween((double a) { _sprite.rotation = a; }, 0.0, 360.0, 1.0);
|
|
actions.run(new ActionRepeatForever(rotate));
|
|
|
|
// Fade in
|
|
ActionTween fadeIn = new ActionTween((double a) { _sprite.opacity = a; }, 0.0, 1.0, 0.6);
|
|
actions.run(fadeIn);
|
|
}
|
|
|
|
Sprite _sprite;
|
|
|
|
void collect() {
|
|
f.playerState.addCoin(this);
|
|
super.collect();
|
|
}
|
|
}
|
|
|
|
enum PowerUpType {
|
|
shield,
|
|
speedLaser,
|
|
sideLaser,
|
|
speedBoost,
|
|
}
|
|
|
|
List<PowerUpType> _powerUpTypes = new List<PowerUpType>.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) {
|
|
_sprite = new Sprite(f.sheet["coin.png"]);
|
|
_sprite.scale = 1.2;
|
|
addChild(_sprite);
|
|
|
|
radius = 10.0;
|
|
}
|
|
|
|
Sprite _sprite;
|
|
PowerUpType type;
|
|
|
|
void setupActions() {
|
|
ActionTween rotate = new ActionTween((double a) { _sprite.rotation = a; }, 0.0, 360.0, 1.0);
|
|
actions.run(new ActionRepeatForever(rotate));
|
|
|
|
// Fade in
|
|
ActionTween fadeIn = new ActionTween((double a) { _sprite.opacity = a; }, 0.0, 1.0, 0.6);
|
|
actions.run(fadeIn);
|
|
}
|
|
|
|
void collect() {
|
|
f.playerState.activatePowerUp(type);
|
|
super.collect();
|
|
}
|
|
}
|