이클립스 사전 경고 연출 · 브리핑 출처·게시시각 · PASSPORT 역사적 사건 Stamp
This commit is contained in:
@@ -137,6 +137,8 @@ class Planet25D extends StatefulWidget {
|
||||
this.isEclipsed = false,
|
||||
this.eclipseFailed = false,
|
||||
this.eclipseSuccessId,
|
||||
this.eclipseWarnAt,
|
||||
this.eclipseStartAt,
|
||||
this.isHibernating = false,
|
||||
this.isHeroTime = false,
|
||||
this.heroEventIds,
|
||||
@@ -170,6 +172,11 @@ class Planet25D extends StatefulWidget {
|
||||
|
||||
/// 이클립스 성공 회차. 바뀌면 광량 복원 뒤 짧은 Orbit Burst(v1.5 §5 「그림자 이탈 → 복원 → Orbit Burst」).
|
||||
final String? eclipseSuccessId;
|
||||
|
||||
/// 사전 경고 창(서버 회차 `warnAt` → `startAt`, 원장 C-22 발생 3분 전). 이 사이에만
|
||||
/// v1.5 §5 「행성 뒤 어두운 그림자 접근, 별빛·Glow 점진 감소」를 그린다([eclipseApproach]).
|
||||
final DateTime? eclipseWarnAt;
|
||||
final DateTime? eclipseStartAt;
|
||||
final bool isHibernating;
|
||||
final bool isHeroTime;
|
||||
final Set<String>? heroEventIds;
|
||||
@@ -204,12 +211,12 @@ class _Planet25DState extends State<Planet25D>
|
||||
double get _light => widget.isHibernating
|
||||
? 0.35
|
||||
: widget.isEclipsed
|
||||
? 0.5
|
||||
? eclipseLight
|
||||
: _celebrating
|
||||
? 1.3
|
||||
: widget.eclipseFailed
|
||||
// 실패의 감소 폭은 원문에 수치가 없어 이클립스 진행 중과 같은 값을 쓴다(해석 확인 항목).
|
||||
? 0.5
|
||||
? eclipseLight
|
||||
: 1;
|
||||
late final AnimationController _c = AnimationController(
|
||||
vsync: this,
|
||||
@@ -376,6 +383,13 @@ class _Planet25DState extends State<Planet25D>
|
||||
builder: (context, _) {
|
||||
final t = _motionEnabled ? _c.value : 0.0; // 0~1
|
||||
final wave = math.sin(t * 2 * math.pi); // -1~1
|
||||
// 사전 경고 — 매 프레임 서버 시각 기준으로 다시 잰다. 동작 줄이기면 프레임이 돌지 않아
|
||||
// 그린 시점의 위치에 멈춰 있다(움직임 없이 상태만 전달).
|
||||
final warn = widget.isEclipsed
|
||||
? 0.0
|
||||
: eclipseApproach(widget.eclipseWarnAt, widget.eclipseStartAt, DateTime.now()) ?? 0.0;
|
||||
// 별빛(입자) · Glow 를 이클립스 진행 중 광량(0.5)까지 점진적으로 낮춘다.
|
||||
final dim = 1 - (1 - eclipseLight) * warn;
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.center,
|
||||
@@ -400,9 +414,10 @@ class _Planet25DState extends State<Planet25D>
|
||||
? 0.55
|
||||
: 0.42) +
|
||||
wave * (stage == 6 ? 0.02 : 0.05)) *
|
||||
glowStrength,
|
||||
glowStrength *
|
||||
dim,
|
||||
),
|
||||
glow.withValues(alpha: 0.10 * glowStrength),
|
||||
glow.withValues(alpha: 0.10 * glowStrength * dim),
|
||||
glow.withValues(alpha: 0),
|
||||
],
|
||||
stops: const [0.45, 0.72, 1],
|
||||
@@ -423,6 +438,31 @@ class _Planet25DState extends State<Planet25D>
|
||||
),
|
||||
),
|
||||
),
|
||||
// 이클립스 사전 경고 — 행성 뒤로 어두운 그림자가 우상단에서 다가와 Glow 를 가린다.
|
||||
if (warn > 0)
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: Transform.translate(
|
||||
key: const ValueKey('eclipse-shadow'),
|
||||
offset: Offset(s, -s * 0.5) * eclipseShadowTravel * (1 - warn),
|
||||
child: Transform.scale(
|
||||
scale: 1.15,
|
||||
child: Opacity(
|
||||
opacity: 0.85 * warn,
|
||||
child: const DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: RadialGradient(
|
||||
colors: [Color(0xFF05040D), Color(0xCC05040D), Color(0x0005040D)],
|
||||
stops: [0, 0.6, 1],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// 6. 링 — 뒤쪽 절반. 본체 뒤에 깔린다.
|
||||
if (ring)
|
||||
Positioned.fill(
|
||||
@@ -522,6 +562,7 @@ class _Planet25DState extends State<Planet25D>
|
||||
count:
|
||||
(particleCount *
|
||||
light *
|
||||
dim *
|
||||
(_quality == 2
|
||||
? 0.3
|
||||
: _quality == 1
|
||||
@@ -883,6 +924,21 @@ class _SustainedAura extends CustomPainter {
|
||||
old.color != color || old.lowEffects != lowEffects;
|
||||
}
|
||||
|
||||
/// 이클립스 진행 중 광량. 사전 경고의 Glow 감소 · 실패 감소가 같은 값을 쓴다(해석 확인 Q-A28).
|
||||
const eclipseLight = 0.5;
|
||||
|
||||
/// 사전 경고 그림자의 출발 거리(행성 한 변 대비). 원문에 수치가 없는 임시값(해석 확인 항목).
|
||||
const eclipseShadowTravel = 0.9;
|
||||
|
||||
/// 사전 경고 진행 0~1 — [warnAt] 에 0, 발생 시각 [startAt] 에 1. 창이 없거나 경고 전이면 null.
|
||||
/// 발생 시각이 지나도 서버가 회차를 열기 전(스케줄러 지연 수 초)에는 1 로 둔다 — 그림자가 빠졌다 다시 어두워지지 않게.
|
||||
double? eclipseApproach(DateTime? warnAt, DateTime? startAt, DateTime now) {
|
||||
if (warnAt == null || startAt == null || !startAt.isAfter(warnAt)) return null;
|
||||
if (now.isBefore(warnAt)) return null;
|
||||
return (now.difference(warnAt).inMilliseconds / startAt.difference(warnAt).inMilliseconds)
|
||||
.clamp(0.0, 1.0);
|
||||
}
|
||||
|
||||
/// 이클립스 성공 Orbit Burst 길이. 원문은 「짧은」만 적어 Level Up 경량 연출(850ms)과 같게 둔다(해석 확인 항목).
|
||||
const orbitBurstDuration = Duration(milliseconds: 850);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user