행성 Level 1~100 수치표 적용 · 행성 컬러 마스터
This commit is contained in:
@@ -8,7 +8,9 @@
|
||||
/// 정책값이 아니다 — 정책은 `정책 설계서 최종본 v1.0` 이 정한다.
|
||||
library;
|
||||
|
||||
import 'package:flutter/painting.dart' show Color;
|
||||
import '../../core/i18n/app_locale.dart';
|
||||
import '../../core/ui/planet_25d.dart' show planetColors;
|
||||
import '../quest/quest_data.dart' show QuestType;
|
||||
import '../inside/inside_data.dart' show InsideQuestion, SampleInsideRepository;
|
||||
|
||||
@@ -414,6 +416,9 @@ abstract interface class HomeRepository {
|
||||
/// 성운 전환 목표 — 서버(`fn-nebula-create` · `fn-planet-create`)와 같은 `config/policy`
|
||||
/// 키(`nebulaMinPop` · `nebulaMinMembers` · `nebulaDays`). 읽지 못하면 [defaultNebulaTargets].
|
||||
Future<NebulaTargets> nebulaTargets();
|
||||
|
||||
/// 행성 생성 화면이 내놓을 컬러 — 컬러 마스터 `config/planetColors`(10/3 #6 · C-115). 읽지 못하면 Figma 기본 8색.
|
||||
Future<List<(String, Color)>> planetColorOptions();
|
||||
}
|
||||
|
||||
/// 성운 전환 목표 — POP · 창립 인원 · 목표 기간(일).
|
||||
@@ -451,6 +456,9 @@ class SampleHomeRepository implements HomeRepository {
|
||||
@override
|
||||
Future<NebulaTargets> nebulaTargets() async => defaultNebulaTargets;
|
||||
|
||||
@override
|
||||
Future<List<(String, Color)>> planetColorOptions() async => planetColors;
|
||||
|
||||
@override
|
||||
Future<({String fandomId, bool primaryChanged})> createNebula(
|
||||
String name,
|
||||
|
||||
@@ -3,6 +3,8 @@ import 'package:cloud_firestore/cloud_firestore.dart' show Timestamp;
|
||||
import '../../core/data/backend.dart';
|
||||
import '../../core/data/invite_code.dart';
|
||||
import '../../core/data/server_clock.dart';
|
||||
import '../../core/ui/planet_25d.dart' show planetColors, planetColorsFrom, setPlanetColorMaster;
|
||||
import 'package:flutter/painting.dart' show Color;
|
||||
import 'home_data.dart';
|
||||
import '../inside/inside_repository_firestore.dart' show insideQuestionFrom;
|
||||
import '../quest/quest_data.dart' show QuestType;
|
||||
@@ -66,6 +68,19 @@ class FirestoreHomeRepository implements HomeRepository {
|
||||
'colorKey': colorKey,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<List<(String, Color)>> planetColorOptions() async {
|
||||
try {
|
||||
final master = planetColorsFrom(
|
||||
(await _backend.db.doc('config/planetColors').get()).data(),
|
||||
);
|
||||
setPlanetColorMaster(master.all);
|
||||
return master.visible;
|
||||
} catch (_) {
|
||||
return planetColors;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<NebulaTargets> nebulaTargets() async {
|
||||
try {
|
||||
@@ -82,6 +97,8 @@ class FirestoreHomeRepository implements HomeRepository {
|
||||
final uid = _backend.requireUid;
|
||||
// 공유 링크 `?ref=` 용 — 기다리지 않는다.
|
||||
InviteCode.ensure(_backend);
|
||||
// 컬러 마스터 — HOME · GALAXY 행성이 발주사가 더한 색도 그리도록 먼저 읽어 둔다. 기다리지 않는다.
|
||||
planetColorOptions();
|
||||
final db = _backend.db;
|
||||
|
||||
final userSnap = await db.doc('users/$uid').get();
|
||||
@@ -291,8 +308,15 @@ class FirestoreHomeRepository implements HomeRepository {
|
||||
int? validInt(dynamic value) => value is int && value >= 0 ? value : null;
|
||||
num? validNumber(dynamic value) =>
|
||||
value is num && value.isFinite && value >= 0 ? value : null;
|
||||
final current = validNumber(fandom['growthPop']);
|
||||
final required = validInt(next?['requiredPop']);
|
||||
// 정식 행성은 서버가 수치표 v1.4 대로 적은 레벨업 POP 진행도(생성 기준점 이후 · ZAP 가중치 반영)와
|
||||
// 다음 Stage 진입 누계를 쓴다. 아직 판정 전인 행성만 구 마스터(`planetStages`)로 그린다.
|
||||
final leveled = stage != null && stage > 0 && fandom['levelPop'] is num;
|
||||
final current = leveled
|
||||
? validNumber(fandom['levelPop'])
|
||||
: validNumber(fandom['growthPop']);
|
||||
final required = leveled
|
||||
? validInt(fandom['nextStagePop'])
|
||||
: validInt(next?['requiredPop']);
|
||||
// 성운(Stage0)의 전환 목표는 서버와 같은 `config/policy` 키로 읽는다.
|
||||
final policy = stage == 0
|
||||
? (await _backend.db.doc('config/policy').get()).data()
|
||||
@@ -325,7 +349,9 @@ class FirestoreHomeRepository implements HomeRepository {
|
||||
// Q-PN-01 A안 — 탄생은 창립자만.
|
||||
isFounder:
|
||||
fandom['founderUid'] != null && fandom['founderUid'] == _backend.uid,
|
||||
requiredSfpi: validNumber(next?['requiredSfpi']),
|
||||
requiredSfpi: leveled
|
||||
? validNumber(fandom['nextStageSfpi'])
|
||||
: validNumber(next?['requiredSfpi']),
|
||||
nebulaGoalPassed: goalPassed,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ class PlanetCreateScreen extends StatefulWidget {
|
||||
'shape6',
|
||||
];
|
||||
|
||||
/// 색 키와 칩 색 — 렌더링과 같은 표([planetColors]).
|
||||
/// 기본 색 키와 칩 색(Figma `2066:17910`). 화면은 컬러 마스터(`config/planetColors`, 10/3 #6)를
|
||||
/// [HomeRepository.planetColorOptions] 로 읽어 덮는다 — 발주사가 더한 색도 고를 수 있다.
|
||||
static const colors = planetColors;
|
||||
|
||||
@override
|
||||
@@ -53,6 +54,19 @@ class _PlanetCreateScreenState extends State<PlanetCreateScreen> {
|
||||
int _shape = 0;
|
||||
int _color = 0;
|
||||
bool _colorStep = false;
|
||||
List<(String, Color)> _colors = PlanetCreateScreen.colors;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget.repository.planetColorOptions().then((options) {
|
||||
if (!mounted || options.isEmpty) return;
|
||||
setState(() {
|
||||
_colors = options;
|
||||
_color = _color.clamp(0, options.length - 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static const _placeholder = AssetImage(
|
||||
'assets/figma/planet-shape-placeholder.png',
|
||||
@@ -72,7 +86,7 @@ class _PlanetCreateScreenState extends State<PlanetCreateScreen> {
|
||||
child: _colorStep
|
||||
? ColorFiltered(
|
||||
colorFilter: ColorFilter.mode(
|
||||
PlanetCreateScreen.colors[_color].$2,
|
||||
_colors[_color].$2,
|
||||
BlendMode.modulate,
|
||||
),
|
||||
child: const Image(image: _placeholder),
|
||||
@@ -141,7 +155,7 @@ class _PlanetCreateScreenState extends State<PlanetCreateScreen> {
|
||||
);
|
||||
|
||||
/// 사본 `Frame 1984081340` — 28 원 · 간격 8 · 고른 칩 분홍 4 테두리, 나머지 흰 2.
|
||||
Widget _colors() => Column(
|
||||
Widget _colorPicker() => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(context.l10n.planetBgColor, style: _heading),
|
||||
@@ -150,19 +164,19 @@ class _PlanetCreateScreenState extends State<PlanetCreateScreen> {
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (var i = 0; i < PlanetCreateScreen.colors.length; i++)
|
||||
for (var i = 0; i < _colors.length; i++)
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => _color = i),
|
||||
child: Semantics(
|
||||
button: true,
|
||||
selected: _color == i,
|
||||
label: PlanetCreateScreen.colors[i].$1,
|
||||
label: _colors[i].$1,
|
||||
child: Container(
|
||||
width: 28,
|
||||
height: 28,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: PlanetCreateScreen.colors[i].$2,
|
||||
color: _colors[i].$2,
|
||||
border: Border.all(
|
||||
color: _color == i
|
||||
? AppColors.cosmicAccent
|
||||
@@ -200,7 +214,7 @@ class _PlanetCreateScreenState extends State<PlanetCreateScreen> {
|
||||
children: [
|
||||
_preview(),
|
||||
const SizedBox(height: AppSpace.s5),
|
||||
if (_colorStep) _colors() else _shapes(),
|
||||
if (_colorStep) _colorPicker() else _shapes(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -257,7 +271,7 @@ class _PlanetCreateScreenState extends State<PlanetCreateScreen> {
|
||||
await widget.repository.createPlanet(
|
||||
fandomId: widget.fandomId,
|
||||
planetType: PlanetCreateScreen.shapes[_shape],
|
||||
colorKey: PlanetCreateScreen.colors[_color].$1,
|
||||
colorKey: _colors[_color].$1,
|
||||
);
|
||||
if (mounted) Navigator.of(context).pop(widget.fandomId);
|
||||
} on BackendException catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user