이클립스 사전 경고 연출 · 브리핑 출처·게시시각 · PASSPORT 역사적 사건 Stamp
This commit is contained in:
@@ -452,6 +452,7 @@ class _EclipseDefendScreenState extends State<EclipseDefendScreen>
|
||||
remaining: remaining,
|
||||
pending: _pending,
|
||||
pulse: _pulse,
|
||||
shield: state.progress,
|
||||
image: _imageOf(widget.nebula),
|
||||
),
|
||||
),
|
||||
@@ -713,8 +714,12 @@ class _DefendPlanet extends StatelessWidget {
|
||||
required this.pending,
|
||||
required this.image,
|
||||
this.pulse = 0,
|
||||
this.shield = 0,
|
||||
});
|
||||
|
||||
/// 고유 유효 참여자 / 목표(서버 값 [EclipseState.progress]) — Shield 조각이 이어지는 정도.
|
||||
final double shield;
|
||||
|
||||
/// 흔들기 일련번호. 0 이면 아직 파동이 없다.
|
||||
final int pulse;
|
||||
|
||||
@@ -767,6 +772,23 @@ class _DefendPlanet extends StatelessWidget {
|
||||
CustomPaint(painter: _RemainingRing(remaining)),
|
||||
),
|
||||
_at(67.5, 300, 240, 240, Image(image: image)),
|
||||
// Shield — 끊어진 Ring 이 참여자 비율만큼 이어지고 목표 달성 시 완전한 원(v1.5 §5.1).
|
||||
// 값이 바뀌면 Shield Pulse 와 같은 길이로 이어 붙인다. 동작 줄이기면 바로 바뀐다.
|
||||
_at(
|
||||
48,
|
||||
280,
|
||||
280,
|
||||
280,
|
||||
TweenAnimationBuilder<double>(
|
||||
key: const ValueKey('shield-ring'),
|
||||
tween: Tween(end: shield.clamp(0.0, 1.0)),
|
||||
duration: MediaQuery.disableAnimationsOf(context)
|
||||
? Duration.zero
|
||||
: shieldPulseDuration,
|
||||
builder: (context, p, _) =>
|
||||
CustomPaint(painter: ShieldRingPainter(p)),
|
||||
),
|
||||
),
|
||||
// Shield Pulse — 흔들기마다 행성 가장자리에서 원형 파동이 한 번 퍼진다. 동작 줄이기면 그리지 않는다.
|
||||
if (pulse > 0 && !MediaQuery.disableAnimationsOf(context))
|
||||
_at(
|
||||
@@ -978,3 +1000,53 @@ class ShieldPulsePainter extends CustomPainter {
|
||||
@override
|
||||
bool shouldRepaint(ShieldPulsePainter old) => old.t != t;
|
||||
}
|
||||
|
||||
/// Shield Ring 조각 수. 원문(v1.5 §5.1)에 수치가 없는 임시값(해석 확인 항목).
|
||||
const shieldSegments = 12;
|
||||
|
||||
/// 진행 [p] 0~1 → Ring 조각들의 (시작각, 길이). 처음엔 조각 사이 틈이 절반이고, 참여자가 늘수록
|
||||
/// 틈이 좁아져 이어지며, 목표 달성(1)이면 틈 없는 원 하나다.
|
||||
List<(double, double)> shieldArcs(double p) {
|
||||
if (p >= 1) return const [(-math.pi / 2, 2 * math.pi)];
|
||||
const slot = 2 * math.pi / shieldSegments;
|
||||
final sweep = slot * (0.5 + 0.5 * p.clamp(0.0, 1.0));
|
||||
return [
|
||||
for (var i = 0; i < shieldSegments; i++)
|
||||
(-math.pi / 2 + i * slot + (slot - sweep) / 2, sweep),
|
||||
];
|
||||
}
|
||||
|
||||
/// 행성(240)과 잔여 시간 원호(280) 사이의 Shield Ring. 완성되면 바깥 번짐을 한 겹 더한다.
|
||||
class ShieldRingPainter extends CustomPainter {
|
||||
const ShieldRingPainter(this.progress);
|
||||
|
||||
final double progress;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final r = size.shortestSide / 2 * (260 / 280);
|
||||
final rect = Rect.fromCircle(center: size.center(Offset.zero), radius: r);
|
||||
final full = progress >= 1;
|
||||
final paint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = full ? 5 : 3
|
||||
..strokeCap = StrokeCap.round
|
||||
..color = AppColors.neonEmerald.withValues(alpha: 0.35 + 0.55 * progress.clamp(0.0, 1.0));
|
||||
if (full) {
|
||||
canvas.drawCircle(
|
||||
rect.center,
|
||||
r,
|
||||
Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 12
|
||||
..color = AppColors.neonEmerald.withValues(alpha: 0.25),
|
||||
);
|
||||
}
|
||||
for (final (start, sweep) in shieldArcs(progress)) {
|
||||
canvas.drawArc(rect, start, sweep, false, paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(ShieldRingPainter old) => old.progress != progress;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user