185 lines
5.1 KiB
Dart
185 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../../providers/settings_provider.dart';
|
|
import '../../game/enums.dart';
|
|
import '../../game/config.dart';
|
|
|
|
class BattleAnimationWidget extends StatefulWidget {
|
|
final Widget child;
|
|
|
|
const BattleAnimationWidget({super.key, required this.child});
|
|
|
|
@override
|
|
BattleAnimationWidgetState createState() => BattleAnimationWidgetState();
|
|
}
|
|
|
|
class BattleAnimationWidgetState extends State<BattleAnimationWidget>
|
|
with TickerProviderStateMixin {
|
|
late AnimationController _scaleController;
|
|
late AnimationController _translateController;
|
|
late Animation<double> _scaleAnimation;
|
|
late Animation<Offset> _translateAnimation;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_scaleController = AnimationController(
|
|
vsync: this,
|
|
duration: AnimationConfig.attackRiskyScale,
|
|
);
|
|
_translateController = AnimationController(
|
|
vsync: this,
|
|
duration: AnimationConfig.attackRiskyDash,
|
|
);
|
|
|
|
_scaleAnimation = Tween<double>(
|
|
begin: 1.0,
|
|
end: 2.0,
|
|
).animate(CurvedAnimation(parent: _scaleController, curve: Curves.easeOut));
|
|
|
|
// Default translation, will be updated on animateAttack
|
|
_translateAnimation = Tween<Offset>(
|
|
begin: Offset.zero,
|
|
end: Offset.zero,
|
|
).animate(_translateController);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_scaleController.dispose();
|
|
_translateController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> animateAttack(
|
|
Offset targetOffset,
|
|
VoidCallback onImpact,
|
|
RiskLevel risk, {
|
|
VoidCallback? onAnimationStart,
|
|
VoidCallback? onAnimationMiddle,
|
|
VoidCallback? onAnimationEnd,
|
|
}) async {
|
|
_resetControllers();
|
|
onAnimationStart?.call();
|
|
|
|
if (risk == RiskLevel.safe || risk == RiskLevel.normal) {
|
|
final isSafe = risk == RiskLevel.safe;
|
|
final duration = AnimationConfig.getAttackDuration(risk);
|
|
final offsetFactor = isSafe ? 0.2 : 0.5;
|
|
final curve = isSafe
|
|
? AnimationConfig.attackSafeCurve
|
|
: AnimationConfig.attackNormalCurve;
|
|
|
|
_translateController.duration = duration;
|
|
_translateAnimation = Tween<Offset>(
|
|
begin: Offset.zero,
|
|
end: targetOffset * offsetFactor,
|
|
).animate(CurvedAnimation(parent: _translateController, curve: curve));
|
|
|
|
await _translateController.forward();
|
|
if (!mounted) return;
|
|
|
|
onAnimationEnd?.call();
|
|
onImpact();
|
|
|
|
await _translateController.reverse();
|
|
} else {
|
|
final attackScale = context.read<SettingsProvider>().attackAnimScale;
|
|
_scaleAnimation = Tween<double>(begin: 1.0, end: attackScale).animate(
|
|
CurvedAnimation(parent: _scaleController, curve: Curves.easeOut),
|
|
);
|
|
|
|
_scaleController.duration = AnimationConfig.attackRiskyScale;
|
|
_translateController.duration = AnimationConfig.attackRiskyDash;
|
|
|
|
await _scaleController.forward();
|
|
if (!mounted) return;
|
|
|
|
onAnimationMiddle?.call();
|
|
|
|
final adjustedOffset = targetOffset * 0.5;
|
|
|
|
_translateAnimation =
|
|
Tween<Offset>(begin: Offset.zero, end: adjustedOffset).animate(
|
|
CurvedAnimation(
|
|
parent: _translateController,
|
|
curve: AnimationConfig.attackRiskyDashCurve,
|
|
),
|
|
);
|
|
|
|
await _translateController.forward();
|
|
if (!mounted) return;
|
|
|
|
onAnimationEnd?.call();
|
|
onImpact();
|
|
|
|
await Future.wait([
|
|
_scaleController.reverse(),
|
|
_translateController.reverse(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
Future<void> animateDefense(VoidCallback onImpact) async {
|
|
_resetControllers();
|
|
_translateController.duration = const Duration(milliseconds: 800);
|
|
|
|
// Sequence: Left -> Right -> Center
|
|
_translateAnimation =
|
|
TweenSequence<Offset>([
|
|
TweenSequenceItem(
|
|
tween: Tween<Offset>(begin: Offset.zero, end: const Offset(-10, 0)),
|
|
weight: 25,
|
|
),
|
|
TweenSequenceItem(
|
|
tween: Tween<Offset>(
|
|
begin: const Offset(-10, 0),
|
|
end: const Offset(10, 0),
|
|
),
|
|
weight: 50,
|
|
),
|
|
TweenSequenceItem(
|
|
tween: Tween<Offset>(begin: const Offset(10, 0), end: Offset.zero),
|
|
weight: 25,
|
|
),
|
|
]).animate(
|
|
CurvedAnimation(
|
|
parent: _translateController,
|
|
curve: Curves.easeInOut,
|
|
),
|
|
);
|
|
|
|
await _translateController.forward();
|
|
if (!mounted) return;
|
|
onImpact();
|
|
_translateController.reset();
|
|
}
|
|
|
|
void _resetControllers() {
|
|
if (_scaleController.isAnimating) {
|
|
_scaleController.stop();
|
|
}
|
|
if (_translateController.isAnimating) {
|
|
_translateController.stop();
|
|
}
|
|
_scaleController.reset();
|
|
_translateController.reset();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: Listenable.merge([_scaleController, _translateController]),
|
|
builder: (context, child) {
|
|
return Transform.translate(
|
|
offset: _translateAnimation.value,
|
|
child: Transform.scale(
|
|
scale: _scaleAnimation.value,
|
|
child: widget.child,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|