mirror of
https://github.com/johrpan/christmas_cats.git
synced 2025-10-28 11:17:25 +01:00
Version 0.1.0
This commit is contained in:
commit
5dda59a6cd
73 changed files with 5741 additions and 0 deletions
91
lib/components/cat.dart
Normal file
91
lib/components/cat.dart
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flame/anchor.dart';
|
||||
import 'package:flame/components/component.dart';
|
||||
import 'package:flame/sprite.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
final random = Random(DateTime.now().millisecondsSinceEpoch);
|
||||
|
||||
class Cat extends PositionComponent {
|
||||
static const vmin = 0.1;
|
||||
static const vmax = 150.0;
|
||||
static const accLength = 20.0;
|
||||
static const minDistance = 4.0;
|
||||
|
||||
final anchor = Anchor.bottomCenter;
|
||||
final renderFlipX = true;
|
||||
final axis = Vector2(-1.0, 0.0);
|
||||
|
||||
final catNumber = random.nextInt(7) + 1;
|
||||
|
||||
void Function() onBored;
|
||||
|
||||
bool shouldDestroy = false;
|
||||
|
||||
Vector2 target;
|
||||
Vector2 position;
|
||||
Vector2 velocity = Vector2.zero();
|
||||
bool bored = false;
|
||||
|
||||
bool get isSitting => velocity.length < vmin;
|
||||
double get width => isSitting ? 25.0 : 40.0;
|
||||
double get height => isSitting ? 18.0 : 18.0;
|
||||
double get x => position.x;
|
||||
double get y => position.y;
|
||||
double get angle => -velocity.angleToSigned(axis);
|
||||
bool get renderFlipY => angle.abs() > 0.5 * pi;
|
||||
|
||||
Sprite sitting;
|
||||
Sprite running;
|
||||
|
||||
Cat() {
|
||||
sitting = Sprite('cat${catNumber}s.png');
|
||||
running = Sprite('cat${catNumber}r.png');
|
||||
}
|
||||
|
||||
void runTo(Vector2 target) {
|
||||
bored = false;
|
||||
this.target = target;
|
||||
|
||||
if (position == null) {
|
||||
position = target;
|
||||
}
|
||||
|
||||
velocity = (target - position).normalized() * vmax;
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
prepareCanvas(canvas);
|
||||
|
||||
if (isSitting) {
|
||||
sitting.render(canvas, width: width, height: height);
|
||||
} else {
|
||||
running.render(canvas, width: width, height: height);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double t) {
|
||||
if ((position - target).length < minDistance) {
|
||||
position = target;
|
||||
velocity.setZero();
|
||||
if (!bored) {
|
||||
bored = true;
|
||||
if (onBored != null) {
|
||||
onBored();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
position += velocity * t;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool destroy() => shouldDestroy;
|
||||
|
||||
@override
|
||||
int priority() => 10000;
|
||||
}
|
||||
65
lib/components/progress.dart
Normal file
65
lib/components/progress.dart
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flame/anchor.dart';
|
||||
import 'package:flame/components/component.dart';
|
||||
import 'package:flame/components/mixins/resizable.dart';
|
||||
import 'package:flame/position.dart';
|
||||
import 'package:flame/text_config.dart';
|
||||
import 'package:sprintf/sprintf.dart';
|
||||
|
||||
import '../localizations.dart';
|
||||
|
||||
class Progress extends Component with Resizable {
|
||||
static const height = 2.0;
|
||||
static const color = Color(0xff000000);
|
||||
|
||||
static const subtileTextConfig = TextConfig(
|
||||
fontSize: 28.0,
|
||||
fontFamily: 'Tangerine',
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
|
||||
final ChristmasCatsLocalizations localizations;
|
||||
final void Function() onComplete;
|
||||
|
||||
int rounds = 0;
|
||||
double progress = 0.0;
|
||||
double seconds = 0.0;
|
||||
bool playing = false;
|
||||
bool completed = false;
|
||||
|
||||
double get endSeconds => 20.0 + rounds * 10.0;
|
||||
int get score => (2 * (rounds * endSeconds + seconds)).floor();
|
||||
|
||||
Progress(this.localizations, this.onComplete);
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
final paint = Paint()..color = color;
|
||||
final width = (seconds / endSeconds) * size.width;
|
||||
canvas.drawRect(Rect.fromLTWH(0, 0, width, height), paint);
|
||||
subtileTextConfig.render(
|
||||
canvas,
|
||||
sprintf(localizations.score, [score]),
|
||||
Position(size.width / 2, 4),
|
||||
anchor: Anchor.topCenter,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
if (playing && !completed) {
|
||||
seconds += dt;
|
||||
if (seconds > endSeconds) {
|
||||
completed = true;
|
||||
if (onComplete != null) {
|
||||
rounds++;
|
||||
seconds = 0;
|
||||
onComplete();
|
||||
}
|
||||
}
|
||||
} else if (seconds < endSeconds) {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
112
lib/components/tree.dart
Normal file
112
lib/components/tree.dart
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import 'dart:ui';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flame/anchor.dart';
|
||||
import 'package:flame/components/component.dart';
|
||||
import 'package:flame/sprite.dart';
|
||||
import 'package:flame/time.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
final random = Random(DateTime.now().millisecondsSinceEpoch);
|
||||
|
||||
class Tree extends PositionComponent {
|
||||
final anchor = Anchor.bottomCenter;
|
||||
final void Function() onDestroy;
|
||||
final treeNumber = random.nextInt(4) + 1;
|
||||
|
||||
bool shouldDestroy = false;
|
||||
Timer nextLevelTimer;
|
||||
Timer killTimer;
|
||||
int shakeLevel = 0;
|
||||
bool shakingLeft = true;
|
||||
double get shakeAngle => shakeLevel * shakeLevel * pi / 1000;
|
||||
|
||||
double get width => treeNumber <= 2 ? 80.0 : 70.0;
|
||||
double get height => treeNumber <= 2 ? 100.0 : 80.0;
|
||||
|
||||
Sprite sprite;
|
||||
|
||||
Tree(Vector2 position, this.onDestroy) {
|
||||
x = position.x;
|
||||
y = position.y;
|
||||
|
||||
sprite = Sprite('tree$treeNumber.png');
|
||||
|
||||
nextLevelTimer = Timer(
|
||||
0.1,
|
||||
repeat: true,
|
||||
callback: () {
|
||||
shakeLevel++;
|
||||
if (shakeLevel > 16 && onDestroy != null) {
|
||||
onDestroy();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
killTimer = Timer(1, callback: () {
|
||||
shouldDestroy = true;
|
||||
});
|
||||
}
|
||||
|
||||
void shake() {
|
||||
if (shakeLevel == 0) {
|
||||
shakeLevel = 1;
|
||||
nextLevelTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
void stopShaking() {
|
||||
shakeLevel = 0;
|
||||
nextLevelTimer.stop();
|
||||
}
|
||||
|
||||
void kill() {
|
||||
shakeLevel = -1;
|
||||
nextLevelTimer.stop();
|
||||
killTimer.start();
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas c) {
|
||||
prepareCanvas(c);
|
||||
sprite.render(c, width: width, height: height);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double t) {
|
||||
nextLevelTimer.update(t);
|
||||
killTimer.update(t);
|
||||
|
||||
if (shakeLevel >= 0) {
|
||||
if ((angle - shakeAngle).abs() > 0.01) {
|
||||
if (shakingLeft) {
|
||||
angle -= t * (shakeLevel + 1) / 3;
|
||||
if (angle < -shakeAngle) {
|
||||
shakingLeft = false;
|
||||
}
|
||||
} else {
|
||||
angle += t * (shakeLevel + 1) / 3;
|
||||
if (angle > shakeAngle) {
|
||||
shakingLeft = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (shakingLeft) {
|
||||
if (angle > -0.5 * pi) {
|
||||
angle -= t * 10;
|
||||
}
|
||||
} else {
|
||||
if (angle < 0.5 * pi) {
|
||||
angle += t * 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool destroy() => shouldDestroy;
|
||||
|
||||
@override
|
||||
int priority() => 20000 + y.floor();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue